4coder/custom/4coder_profile_inspect.cpp

367 lines
13 KiB
C++

/*
* 4coder_profile.cpp - Built in self profiling UI.
*/
// TOP
struct Profile_Group_Ptr{
Profile_Group_Ptr *next;
Profile_Group *group;
};
struct Profile_Universal_Slot{
Profile_Universal_Slot *next;
String_Const_u8 source_location;
u32 slot_index;
String_Const_u8 name;
u32 count;
u64 total_time;
};
struct Profile_Thread{
Profile_Thread *next;
i32 thread_id;
Profile_Group_Ptr *first_group;
Profile_Group_Ptr *last_group;
Profile_Universal_Slot *first_slot;
Profile_Universal_Slot *last_slot;
i32 slot_count;
Profile_Universal_Slot **sorted_slots;
};
////////////////////////////////
function Profile_Thread*
get_column_from_thread_id(Profile_Thread *first, i32 thread_id){
Profile_Thread *result = 0;
for (Profile_Thread *node = first;
node != 0;
node = node->next){
if (node->thread_id == thread_id){
result = node;
break;
}
}
return(result);
}
function Profile_Universal_Slot*
get_universal_slot(Profile_Thread *column, String_Const_u8 source_location,
u32 slot_index){
Profile_Universal_Slot *result = 0;
for (Profile_Universal_Slot *node = column->first_slot;
node != 0;
node = node->next){
if (node->slot_index == slot_index &&
string_match(node->source_location, source_location)){
result = node;
break;
}
}
return(result);
}
function void
sort_universal_slots(Profile_Universal_Slot **slots, i32 first, i32 one_past_last){
if (first + 1 < one_past_last){
i32 pivot = one_past_last - 1;
Profile_Universal_Slot *pivot_slot = slots[pivot];
i32 j = first;
for (i32 i = first; i < pivot; i += 1){
Profile_Universal_Slot *slot = slots[i];
b32 is_less = false;
if (slot->total_time < pivot_slot->total_time){
is_less = true;
}
else if (slot->total_time == pivot_slot->total_time){
if (slot->count < pivot_slot->count){
is_less = true;
}
else if (slot->count == pivot_slot->count){
i32 comp = string_compare(slot->source_location,
pivot_slot->source_location);
if (comp < 0){
is_less = true;
}
else if (comp == 0){
if (slot->slot_index < pivot_slot->slot_index){
is_less = true;
}
}
}
}
if (is_less){
Swap(Profile_Universal_Slot*, slots[i], slots[j]);
j += 1;
}
}
Swap(Profile_Universal_Slot*, slots[j], slots[pivot]);
sort_universal_slots(slots, first, pivot);
sort_universal_slots(slots, pivot + 1, one_past_last);
}
}
function void
profile_render(Application_Links *app, Frame_Info frame_info, View_ID view){
Scratch_Block scratch(app);
Rect_f32 region = draw_background_and_margin(app, view);
Rect_f32 prev_clip = draw_set_clip(app, region);
Face_ID face_id = get_face_id(app, 0);
Face_Metrics metrics = get_face_metrics(app, face_id);
f32 line_height = metrics.line_height;
f32 normal_advance = metrics.normal_advance;
f32 block_height = line_height*2.f;
system_mutex_acquire(profile_history.mutex);
// TODO(allen): cache this result!
Profile_Thread *thread_first = 0;
Profile_Thread *thread_last = 0;
i32 thread_count = 0;
for (Profile_Group *node = profile_history.first;
node != 0;
node = node->next){
i32 thread_id = node->thread_id;
Profile_Thread *column = get_column_from_thread_id(thread_first, thread_id);
if (column == 0){
column = push_array_zero(scratch, Profile_Thread, 1);
sll_queue_push(thread_first, thread_last, column);
thread_count += 1;
column->thread_id = thread_id;
}
Profile_Group_Ptr *ptr = push_array(scratch, Profile_Group_Ptr, 1);
sll_queue_push(column->first_group, column->last_group, ptr);
ptr->group = node;
String_Const_u8 source_location = node->source_location;
for (Profile_Record *record = node->first;
record != 0;
record = record->next){
Profile_Universal_Slot *univ_slot =
get_universal_slot(column, source_location, record->slot_index);
if (univ_slot == 0){
univ_slot = push_array(scratch, Profile_Universal_Slot, 1);
sll_queue_push(column->first_slot, column->last_slot, univ_slot);
column->slot_count += 1;
univ_slot->source_location = source_location;
univ_slot->slot_index = record->slot_index;
univ_slot->name = node->slot_names[univ_slot->slot_index];
univ_slot->count = 0;
univ_slot->total_time = 0;
}
univ_slot->count += 1;
univ_slot->total_time += record->time;
}
}
for (Profile_Thread *column = thread_first;
column != 0;
column = column->next){
i32 count = column->slot_count;
Profile_Universal_Slot **slots = push_array(scratch, Profile_Universal_Slot*, count);
column->sorted_slots = slots;
i32 counter = 0;
for (Profile_Universal_Slot *node = column->first_slot;
node != 0;
node = node->next){
slots[counter] = node;
counter += 1;
}
sort_universal_slots(slots, 0, count);
}
f32 column_width = rect_width(region)/(f32)thread_count;
Rect_f32_Pair header_body = rect_split_top_bottom(region, block_height);
Range_f32 full_y = rect_range_y(region);
Range_f32 header_y = rect_range_y(header_body.min);
Range_f32 body_y = rect_range_y(header_body.max);
f32 pos_x = region.x0;
for (Profile_Thread *column = thread_first;
column != 0;
column = column->next){
Range_f32 column_x = If32_size(pos_x, column_width);
Range_f32 text_x = If32(column_x.min + 6.f, column_x.max - 6.f);
f32 text_width = range_size(text_x);
f32 count_width = normal_advance*6.f;
f32 time_width = normal_advance*9.f;
f32 half_padding = normal_advance*0.25f;
f32 label_width = text_width - count_width - time_width;
if (label_width < normal_advance*10.f){
f32 count_ratio = 6.f/25.f;
f32 time_ratio = 9.f/25.f;
f32 label_ratio = 10.f/25.f;
count_width = text_width*count_ratio;
time_width = text_width*time_ratio;
label_width = text_width*label_ratio;
}
i32 count = column->slot_count;
draw_set_clip(app, Rf32(column_x, full_y));
// NOTE(allen): header
{
Rect_f32 box = Rf32(column_x, header_y);
draw_rectangle_outline(app, box, 6.f, 3.f, Stag_Margin_Active);
}
// NOTE(allen): list
{
f32 pos_y = body_y.min;
Profile_Universal_Slot **slot_ptr = column->sorted_slots;
for (i32 i = 0; i < count; i += 1, slot_ptr += 1){
Range_f32 slot_y = If32_size(pos_y, block_height);
Rect_f32 box = Rf32(column_x, slot_y);
draw_rectangle_outline(app, box, 6.f, 3.f, Stag_Margin);
pos_y = slot_y.max;
}
}
// NOTE(allen): header text
{
draw_set_clip(app, Rf32(text_x, full_y));
Fancy_String_List list = {};
push_fancy_stringf(scratch, &list, fancy_id(Stag_Keyword),
"%d", column->thread_id);
f32 y = (header_y.min + header_y.max - line_height)*0.5f;
draw_fancy_string(app, face_id, list.first, V2f32(text_x.min, y), Stag_Default, 0);
}
// NOTE(allen): list text counts
{
Range_f32 x = If32_size(text_x.min + label_width + time_width + half_padding, count_width);
f32 pos_y = body_y.min;
Profile_Universal_Slot **slot_ptr = column->sorted_slots;
for (i32 i = 0; i < count; i += 1, slot_ptr += 1){
Range_f32 slot_y = If32_size(pos_y, block_height);
f32 y = (slot_y.min + slot_y.max - line_height)*0.5f;
Profile_Universal_Slot *slot = *slot_ptr;
Fancy_String_List list = {};
push_fancy_stringf(scratch, &list, fancy_id(Stag_Pop1),
"%5u", slot->count);
draw_fancy_string(app, face_id, list.first, V2f32(x.min, y), Stag_Default, 0);
pos_y = slot_y.max;
}
}
// NOTE(allen): list text labels
{
Range_f32 x = If32_size(text_x.min + label_width + half_padding, time_width - half_padding);
draw_set_clip(app, Rf32(x, full_y));
f32 pos_y = body_y.min;
Profile_Universal_Slot **slot_ptr = column->sorted_slots;
for (i32 i = 0; i < count; i += 1, slot_ptr += 1){
Range_f32 slot_y = If32_size(pos_y, block_height);
f32 y = (slot_y.min + slot_y.max - line_height)*0.5f;
Profile_Universal_Slot *slot = *slot_ptr;
Fancy_String_List list = {};
push_fancy_stringf(scratch, &list, fancy_id(Stag_Pop2),
"%-8.6f", (f32)(slot->total_time)/1000000.f);
draw_fancy_string(app, face_id, list.first, V2f32(x.min, y), Stag_Default, 0);
pos_y = slot_y.max;
}
}
// NOTE(allen): list text labels
{
Range_f32 x = If32_size(text_x.min, label_width - half_padding);
draw_set_clip(app, Rf32(x, full_y));
f32 pos_y = body_y.min;
Profile_Universal_Slot **slot_ptr = column->sorted_slots;
for (i32 i = 0; i < count; i += 1, slot_ptr += 1){
Range_f32 slot_y = If32_size(pos_y, block_height);
f32 y = (slot_y.min + slot_y.max - line_height)*0.5f;
Profile_Universal_Slot *slot = *slot_ptr;
Fancy_String_List list = {};
push_fancy_stringf(scratch, &list, fancy_id(Stag_Default),
"%.*s ", string_expand(slot->name));
draw_fancy_string(app, face_id, list.first, V2f32(x.min, y), Stag_Default, 0);
pos_y = slot_y.max;
}
}
pos_x = column_x.max;
}
system_mutex_release(profile_history.mutex);
draw_set_clip(app, prev_clip);
}
CUSTOM_COMMAND_SIG(profile_inspect)
CUSTOM_DOC("Inspect all currently collected profiling information in 4coder's self profiler.")
{
View_ID view = get_active_view(app, Access_Always);
View_Context ctx = view_current_context(app, view);
ctx.render_caller = profile_render;
ctx.hides_buffer = true;
view_push_context(app, view, &ctx);
profile_history_set_enabled(false, ProfileEnable_InspectBit);
for (;;){
User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape);
if (in.abort){
break;
}
#if 0
b32 handled = true;
switch (in.event.kind){
default:
{
handled = false;
}break;
}
#else
b32 handled = false;
#endif
if (!handled){
// TODO(allen): dedup this stuff.
// TODO(allen): get mapping and map from a more flexible source.
Mapping *mapping = &framework_mapping;
Command_Map *map = mapping_get_map(mapping, mapid_global);
if (mapping != 0 && map != 0){
Command_Binding binding =
map_get_binding_recursive(mapping, map, &in.event);
if (binding.custom != 0){
i64 old_num = get_current_input_sequence_number(app);
binding.custom(app);
i64 num = get_current_input_sequence_number(app);
if (old_num < num){
break;
}
}
else{
leave_current_input_unhandled(app);
}
}
else{
leave_current_input_unhandled(app);
}
}
}
profile_history_set_enabled(true, ProfileEnable_InspectBit);
view_pop_context(app, view);
}
// BOTTOM