Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ncham/duration #31

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
add_subdirectory(tutorials)

1 change: 1 addition & 0 deletions examples/tutorials/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(hello_world)
add_subdirectory(simple_duration)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if tutorials/ is the best place for it, as we don't have article in the documentation for this feature. You could move it to examples/duration_feature, and once we have an article for it, we can move it back to tutorials

2 changes: 2 additions & 0 deletions examples/tutorials/simple_duration/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_executable(simple_duration simple_duration.cpp)
target_link_libraries(simple_duration hawktracer)
43 changes: 43 additions & 0 deletions examples/tutorials/simple_duration/simple_duration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <hawktracer.h>
#include <unistd.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you need it for sleep()? I'd rather avoid it as it will cause build failure on windows. For C++ files we do use C++11 standard (especially for examples/ and tests/) so feel free to use chrono & thread

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) sure I didn't actually notice this was c++... I'll change that.


int main(int argc, char** argv)
{
ht_init(argc, argv);

HT_FileDumpListener* file_dump_listener = ht_file_dump_listener_create("test_output.htdump", 4096u, NULL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the output file name contain an example name?

if (file_dump_listener != NULL)
{
ht_timeline_register_listener(ht_global_timeline_get(), ht_file_dump_listener_callback, file_dump_listener);
}

ht_feature_duration_enable(ht_global_timeline_get());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function might fail. I know it's very unlikely, and the error code will be mostly ignored in that case, but since it's an example, I'd prefer to capture all possible errors here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think we might actually enable this feature in global timeline by default (see global_timeline.cpp file, we already enable e.g. callstack feature)


const int NUM_MEASUREMENTS = 10;
HT_DurationId ids[NUM_MEASUREMENTS];
char names[NUM_MEASUREMENTS][128];

printf ("Gathering %d measurements\n", NUM_MEASUREMENTS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't put spaces between function name and (

for (int i = 0; i < NUM_MEASUREMENTS; i++)
{
sprintf(names[i], "Measurement_name_%d", i);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use snprintf? I know it's safe in this case, but I'm afraid some of the static code analysis tools might complain about using unsafe function

printf("Starting measurement %d, named %s\n", i, names[i]);
ids[i] = ht_feature_duration_start_string(ht_global_timeline_get(), names[i]);
sleep(1);
}

for (int i = 0; i < NUM_MEASUREMENTS; i++)
{
HT_DurationNs duration = ht_feature_duration_stop(ht_global_timeline_get(), ids[i]);
printf("ID: %llu, Duration: %llu nanoseconds.\n", ids[i], duration);
}

ht_timeline_flush(ht_global_timeline_get());
ht_timeline_unregister_all_listeners(ht_global_timeline_get());
ht_file_dump_listener_destroy(file_dump_listener);
ht_feature_duration_disable(ht_global_timeline_get());

ht_deinit();

return 0;
}
2 changes: 2 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(HAWKTRACER_CORE_HEADERS
include/hawktracer/events.h
include/hawktracer/feature_cached_string.h
include/hawktracer/feature_callstack.h
include/hawktracer/feature_duration.h
include/hawktracer/global_timeline.h
include/hawktracer/init.h
include/hawktracer/listener_buffer.h
Expand Down Expand Up @@ -44,6 +45,7 @@ set(HAWKTRACER_CORE_SOURCES
events.c
feature_cached_string.c
feature_callstack.c
feature_duration.c
global_timeline.cpp
init.c
listener_buffer.c
Expand Down
23 changes: 17 additions & 6 deletions lib/bag.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include "internal/bag.h"
#include "hawktracer/alloc.h"

#include <string.h>

static inline HT_Boolean
_ht_bag_resize(HT_Bag* bag, size_t new_capacity)
{
void** ptr = (void**)ht_realloc(bag->data, new_capacity * sizeof(void*));
void* ptr = ht_realloc(bag->data, new_capacity * bag->element_size);

if (ptr == NULL)
{
Expand All @@ -18,9 +20,9 @@ _ht_bag_resize(HT_Bag* bag, size_t new_capacity)
}

HT_ErrorCode
ht_bag_init(HT_Bag* bag, size_t min_capacity)
ht_bag_init(HT_Bag* bag, size_t min_capacity, size_t element_size)
{
bag->data = ht_alloc(min_capacity * sizeof(void*));
bag->data = ht_alloc(min_capacity * element_size);

if (bag->data == NULL)
{
Expand All @@ -30,6 +32,7 @@ ht_bag_init(HT_Bag* bag, size_t min_capacity)
bag->min_capacity = min_capacity;
bag->capacity = min_capacity;
bag->size = 0;
bag->element_size = element_size;

return HT_ERR_OK;
}
Expand All @@ -40,7 +43,7 @@ ht_bag_remove(HT_Bag* bag, void* data)
size_t i;
for (i = 0; i < bag->size; i++)
{
if (bag->data[i] == data)
if (!memcmp(ht_bag_get_n(bag, i), data, bag->element_size))
{
ht_bag_remove_nth(bag, i);
}
Expand All @@ -53,7 +56,7 @@ ht_bag_remove_nth(HT_Bag* bag, size_t n)
bag->size--;
if (bag->size > 0)
{
bag->data[n] = bag->data[bag->size];
memcpy(ht_bag_get_n(bag, n), ht_bag_get_n(bag, bag->size), bag->element_size);
}

if (bag->capacity > bag->min_capacity && bag->size < bag->capacity / 4)
Expand All @@ -73,7 +76,8 @@ ht_bag_add(HT_Bag* bag, void* data)
}
}

bag->data[bag->size++] = data;
memcpy(ht_bag_get_n(bag, bag->size), data, bag->element_size);
bag->size++;

return HT_ERR_OK;
}
Expand All @@ -90,6 +94,13 @@ ht_bag_deinit(HT_Bag* bag)
{
ht_free(bag->data);
bag->data = NULL;
bag->element_size = 0;
bag->capacity = 0;
bag->size = 0;
}

void*
ht_bag_get_n(HT_Bag* bag, size_t n)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be ht_bag_get_nth?

{
return HT_PTR_ADD(bag->data, n * bag->element_size);
}
6 changes: 3 additions & 3 deletions lib/feature_cached_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ ht_feature_cached_string_enable(HT_Timeline* timeline)
return HT_ERR_OK;
}

error_code = ht_bag_init(&feature->cached_data, 1024);
error_code = ht_bag_init(&feature->cached_data, 1024, sizeof(void*));

if (error_code != HT_ERR_OK)
{
Expand Down Expand Up @@ -65,7 +65,7 @@ ht_feature_cached_string_add_mapping(HT_Timeline* timeline, const char* label)
assert(f);

ht_mutex_lock(f->lock);
error_code = ht_bag_add(&f->cached_data, (void*)label);
error_code = ht_bag_add(&f->cached_data, (void*)&label);
ht_mutex_unlock(f->lock);
if (error_code != HT_ERR_OK)
{
Expand All @@ -89,7 +89,7 @@ ht_feature_cached_string_push_map(HT_Timeline* timeline)

for (i = 0; i < f->cached_data.size; i++)
{
HT_TIMELINE_PUSH_EVENT(timeline, HT_StringMappingEvent, (uintptr_t)f->cached_data.data[i], f->cached_data.data[i]);
HT_TIMELINE_PUSH_EVENT(timeline, HT_StringMappingEvent, *(uintptr_t*)ht_bag_get_n(&f->cached_data, i), *(void**)ht_bag_get_n(&f->cached_data, i));
}

ht_mutex_unlock(f->lock);
Expand Down
80 changes: 80 additions & 0 deletions lib/feature_duration.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "hawktracer/feature_duration.h"
#include "hawktracer/alloc.h"
#include "hawktracer/thread.h"
#include "internal/bag.h"

typedef struct
{
HT_Bag bag;
} HT_FeatureDuration;

HT_ErrorCode
ht_feature_duration_enable(HT_Timeline* timeline)
{
HT_FeatureDuration* feature = HT_CREATE_TYPE(HT_FeatureDuration);
HT_ErrorCode error_code;

if (feature == NULL)
{
return HT_ERR_OUT_OF_MEMORY;
}

error_code = ht_bag_init(&feature->bag, 1024, sizeof(HT_DurationStringEvent));

if (error_code != HT_ERR_OK)
{
ht_free(feature);
return error_code;
}

ht_timeline_set_feature(timeline, HT_FEATURE_DURATION, feature);

return HT_ERR_OK;
}

void
ht_feature_duration_disable(HT_Timeline* timeline)
{
HT_FeatureDuration* f = HT_TIMELINE_FEATURE(timeline, HT_FEATURE_DURATION, HT_FeatureDuration);
ht_bag_deinit(&f->bag);
ht_free(f);
ht_timeline_set_feature(timeline, HT_FEATURE_DURATION, NULL);
}

HT_DurationId ht_feature_duration_start(HT_Timeline* timeline, HT_DurationBaseEvent* event)
{
HT_FeatureDuration* f = HT_TIMELINE_FEATURE(timeline, HT_FEATURE_DURATION, HT_FeatureDuration);

ht_timeline_init_event(timeline, HT_EVENT(event));
/* TODO: handle ht_bag_add() error */
ht_bag_add(&f->bag, event);
return f->bag.size - 1;
}

HT_DurationNs ht_feature_duration_stop(HT_Timeline* timeline, HT_DurationId id)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if you want this to return duration?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's very handy to have this duration as a return value

{
HT_FeatureDuration* f = HT_TIMELINE_FEATURE(timeline, HT_FEATURE_DURATION, HT_FeatureDuration);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you've copied it from callstack feature, and I'm ok with merging it as it is. But I think it'd be better to not generate any event here, but just return the value, and it's caller's responsibility to create an event. Current implementation is quite convenient, but from the other hand it always generates an event which is a bit problematic because:

  • we might not want to have the event
  • we can only have 2 types events (int duration and string duration) and it's impossible to extend it without modifying the library.

Maybe we could have base functionality which doesn't generate any event, but just returns the duration, and on top of it, build a feature which generates actual events? Then it'd be easy for user to extend it. Ofc. we have the same problem in callstack feature.
As I said, we can leave it as it is for now, and discuss how to make those features better in the future (ideally, before API lock).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I just copied this behaviour but see your point. If we don't create the event here then I can simplify this PR quite a lot since i'd only need to store a timestamp in the bag.

HT_DurationBaseEvent* event = (HT_DurationBaseEvent*) ht_bag_get_n(&f->bag, id);

HT_DurationNs duration = ht_monotonic_clock_get_timestamp() - HT_EVENT(event)->timestamp;
event->duration = duration;
ht_timeline_push_event((HT_Timeline*)timeline, HT_EVENT(event));
ht_bag_remove_nth(&f->bag, id);
return duration;
}

HT_DurationId ht_feature_duration_start_int(HT_Timeline* timeline, HT_DurationEventLabel label)
{
HT_DECL_EVENT(HT_DurationIntEvent, event);
event.label = label;

return ht_feature_duration_start(timeline, (HT_DurationBaseEvent*)&event);
}

HT_DurationId ht_feature_duration_start_string(HT_Timeline* timeline, const char* label)
{
HT_DECL_EVENT(HT_DurationStringEvent, event);
event.label = label;

return ht_feature_duration_start(timeline, (HT_DurationBaseEvent*)&event);
}
1 change: 1 addition & 0 deletions lib/include/hawktracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <hawktracer/events.h>
#include <hawktracer/feature_cached_string.h>
#include <hawktracer/feature_callstack.h>
#include <hawktracer/feature_duration.h>
#include <hawktracer/global_timeline.h>
#include <hawktracer/init.h>
#include <hawktracer/listeners.h>
Expand Down
2 changes: 2 additions & 0 deletions lib/include/hawktracer/base_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ typedef uint32_t HT_EventKlassId;
typedef uint64_t HT_TimestampNs;
/** An unsigned integer used for event identifiers. */
typedef uint64_t HT_EventId;
/** An unsigned integer used for duration identifiers. */
typedef uint64_t HT_DurationId;
/** An unsigned integer used for representing duration in nanoseconds. */
typedef uint64_t HT_DurationNs;
/** A standard boolean type, possible values: #HT_TRUE, #HT_FALSE */
Expand Down
11 changes: 11 additions & 0 deletions lib/include/hawktracer/core_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ HT_DECLARE_EVENT_KLASS(HT_CallstackIntEvent, HT_CallstackBaseEvent,
HT_DECLARE_EVENT_KLASS(HT_CallstackStringEvent, HT_CallstackBaseEvent,
(STRING, const char*, label))

HT_DECLARE_EVENT_KLASS(HT_DurationBaseEvent, HT_Event,
(INTEGER, HT_DurationNs, duration))
#define HT_DURATION_BASE_EVENT(event) ((HT_DurationBaseEvent*)event)

typedef uint64_t HT_DurationEventLabel;
HT_DECLARE_EVENT_KLASS(HT_DurationIntEvent, HT_DurationBaseEvent,
(INTEGER, HT_DurationEventLabel, label))

HT_DECLARE_EVENT_KLASS(HT_DurationStringEvent, HT_DurationBaseEvent,
(STRING, const char*, label))

HT_DECLARE_EVENT_KLASS(HT_StringMappingEvent, HT_Event,
(INTEGER, uint64_t, identifier),
(STRING, const char*, label))
Expand Down
25 changes: 25 additions & 0 deletions lib/include/hawktracer/feature_duration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef HT_FEATURE_DURATION_H
#define HT_FEATURE_DURATION_H

#include <hawktracer/core_events.h>
#include <hawktracer/timeline.h>

HT_DECLS_BEGIN

#define HT_FEATURE_DURATION 2

HT_API HT_ErrorCode ht_feature_duration_enable(HT_Timeline* timeline);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that currently we don't have documentation for most of the API, but we try add it at least to a new code. So I'd be very grateful if you could add the documentation here :)


HT_API void ht_feature_duration_disable(HT_Timeline* timeline);

HT_API HT_DurationId ht_feature_duration_start(HT_Timeline* timeline, HT_DurationBaseEvent* event);

HT_API HT_DurationNs ht_feature_duration_stop(HT_Timeline* timeline, HT_DurationId id);

HT_API HT_DurationId ht_feature_duration_start_int(HT_Timeline* timeline, HT_DurationEventLabel label);

HT_API HT_DurationId ht_feature_duration_start_string(HT_Timeline* timeline, const char* label);

HT_DECLS_END

#endif /* HT_FEATURE_DURATION_H */
3 changes: 3 additions & 0 deletions lib/include/hawktracer/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
/** Helper macro for suppressing unused value warnings. */
#define HT_UNUSED(x) (void)(x)

/** Helper macro to do pointer arithmetic on HT_Byte* data */
#define HT_PTR_ADD(ptr, value) (((HT_Byte*)ptr) + value)

/** @def HT_DECLS_BEGIN
* Defines a beginning of C linkage block.
*/
Expand Down
10 changes: 7 additions & 3 deletions lib/include/internal/bag.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,23 @@ typedef struct
size_t min_capacity;
size_t capacity;
size_t size;
void** data;
size_t element_size;
void* data;
} HT_Bag;

/**
* Initializes HT_Bag structure.
*
* @param bag a pointer to the container.
* @param min_capacity minimal capacity of the container.
* @param element_size size of each element in the bag.
*
* @returns #HT_ERR_OK, if initialization completed successfully; otherwise, appropriate error code.
*
* Initialization might fail, if the process can't allocate enough memory.
* In that case, #HT_ERR_OUT_OF_MEMORY is returned.
*/
HT_API HT_ErrorCode ht_bag_init(HT_Bag* bag, size_t min_capacity);
HT_API HT_ErrorCode ht_bag_init(HT_Bag* bag, size_t min_capacity, size_t element_size);

/**
* Uninitializes bag structure.
Expand Down Expand Up @@ -93,7 +95,9 @@ HT_API void ht_bag_clear(HT_Bag* bag);
* @warning If the @a bag is empty, behavior of this function
* is undefined.
*/
#define ht_bag_last(bag) bag.data[bag.size - 1]
#define ht_bag_last(bag) HT_PTR_ADD(bag.data, (bag.size - 1) * bag.element_size)

HT_API void* ht_bag_get_n(HT_Bag* bag, size_t n);

HT_DECLS_END

Expand Down
4 changes: 1 addition & 3 deletions lib/include/internal/stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ typedef struct
size_t capacity;
} HT_Stack;

#define HT_PTR_ADD(ptr, value) (((HT_Byte*)ptr) + value)

HT_API HT_ErrorCode ht_stack_init(HT_Stack* stack, size_t capacity, size_t n_capacity);

HT_API void ht_stack_deinit(HT_Stack* stack);
Expand All @@ -26,7 +24,7 @@ HT_API HT_ErrorCode ht_stack_push(HT_Stack* stack, void* data, size_t size);
HT_API void ht_stack_pop(HT_Stack* stack);

#define ht_stack_top(stack) \
HT_PTR_ADD((stack)->data, (size_t)ht_bag_last((stack)->sizes_stack))
HT_PTR_ADD((stack)->data, *(size_t*)ht_bag_last((stack)->sizes_stack))

HT_DECLS_END

Expand Down
3 changes: 3 additions & 0 deletions lib/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ ht_init(int argc, char** argv)
ht_HT_CallstackBaseEvent_register_event_klass();
ht_HT_CallstackIntEvent_register_event_klass();
ht_HT_CallstackStringEvent_register_event_klass();
ht_HT_DurationBaseEvent_register_event_klass();
ht_HT_DurationIntEvent_register_event_klass();
ht_HT_DurationStringEvent_register_event_klass();
ht_HT_StringMappingEvent_register_event_klass();
ht_HT_SystemInfoEvent_register_event_klass();

Expand Down
Loading