418 lines
16 KiB
C++
418 lines
16 KiB
C++
|
/*
|
||
|
* Mr. 4th Dimention - Allen Webster
|
||
|
*
|
||
|
* 14.08.2019
|
||
|
*
|
||
|
* Log parser.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
// TOP
|
||
|
|
||
|
internal u64
|
||
|
log_parse__string_code(Log_Parse *parse, String_Const_u8 string, Log_String_Source string_source){
|
||
|
u64 result = 0;
|
||
|
if (string.size > 0){
|
||
|
Data data = make_data(string.str, string.size);
|
||
|
Table_Lookup lookup = table_lookup(&parse->string_to_id_table, data);
|
||
|
if (lookup.found_match){
|
||
|
table_read(&parse->string_to_id_table, lookup, &result);
|
||
|
}
|
||
|
else{
|
||
|
if (string_source == LogParse_ExternalString){
|
||
|
data = push_data_copy(parse->arena, data);
|
||
|
}
|
||
|
result = parse->string_id_counter;
|
||
|
parse->string_id_counter += 1;
|
||
|
table_insert(&parse->string_to_id_table, data, result);
|
||
|
table_insert(&parse->id_to_string_table, result, data);
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String_Const_u8
|
||
|
log_parse__get_string(Log_Parse *parse, u64 code){
|
||
|
Table_Lookup lookup = table_lookup(&parse->id_to_string_table, code);
|
||
|
String_Const_u8 result = {};
|
||
|
if (lookup.found_match){
|
||
|
Data val = {};
|
||
|
table_read(&parse->id_to_string_table, lookup, &val);
|
||
|
result = SCu8(val.data, val.size);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Log_Event*
|
||
|
log_parse__event(Log_Parse *parse,
|
||
|
String_Const_u8 file_name, String_Const_u8 line_number, String_Const_u8 event_name){
|
||
|
Log_Event *new_event = push_array(parse->arena, Log_Event, 1);
|
||
|
sll_queue_push(parse->first_event, parse->last_event, new_event);
|
||
|
parse->event_count += 1;
|
||
|
new_event->src_file_name = log_parse__string_code(parse, file_name, LogParse_ExternalString);
|
||
|
new_event->event_name = log_parse__string_code(parse, event_name, LogParse_ExternalString);
|
||
|
new_event->line_number = string_to_integer(line_number, 10);
|
||
|
new_event->event_number = parse->event_count;
|
||
|
return(new_event);
|
||
|
}
|
||
|
|
||
|
internal Log_Tag*
|
||
|
log_parse__tag(Log_Parse *parse, Log_Event *event, String_Const_u8 tag_name, String_Const_u8 tag_value){
|
||
|
Log_Tag *new_tag = push_array(parse->arena, Log_Tag, 1);
|
||
|
sll_queue_push(event->first_tag, event->last_tag, new_tag);
|
||
|
event->tag_count += 1;
|
||
|
new_tag->name = log_parse__string_code(parse, tag_name, LogParse_ExternalString);
|
||
|
if (tag_value.size == 0){
|
||
|
new_tag->value.kind = LogTagKind_String;
|
||
|
new_tag->value.value = 0;
|
||
|
}
|
||
|
else{
|
||
|
if (tag_value.str[0] == '"'){
|
||
|
if (tag_value.size == 1){
|
||
|
new_tag->value.kind = LogTagKind_String;
|
||
|
new_tag->value.value = 0;
|
||
|
}
|
||
|
else{
|
||
|
tag_value = string_skip(tag_value, 1);
|
||
|
if (tag_value.str[tag_value.size - 1] == '"'){
|
||
|
tag_value = string_chop(tag_value, 1);
|
||
|
}
|
||
|
String_Const_u8 escape = string_interpret_escapes(parse->arena, tag_value);
|
||
|
new_tag->value.kind = LogTagKind_String;
|
||
|
new_tag->value.value = log_parse__string_code(parse, escape, LogParse_PreAllocatedString);
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
new_tag->value.kind = LogTagKind_Integer;
|
||
|
b32 is_negative = false;
|
||
|
if (string_match(string_prefix(tag_value, 1), string_u8_litexpr("-"))){
|
||
|
tag_value = string_skip(tag_value, 1);
|
||
|
is_negative = true;
|
||
|
}
|
||
|
if (string_match(string_prefix(tag_value, 2), string_u8_litexpr("0x"))){
|
||
|
tag_value = string_skip(tag_value, 2);
|
||
|
new_tag->value.value_s = (i64)string_to_integer(tag_value, 16);
|
||
|
}
|
||
|
else{
|
||
|
new_tag->value.value_s = (i64)string_to_integer(tag_value, 10);
|
||
|
}
|
||
|
if (is_negative){
|
||
|
new_tag->value.value_s = -new_tag->value.value_s;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(new_tag);
|
||
|
}
|
||
|
|
||
|
internal Log_Event_List*
|
||
|
log_parse_get_list_tag_value(Log_Parse *parse, u64 name, Log_Tag_Value value){
|
||
|
Log_Event_List *result = 0;
|
||
|
Log_Tag_Name_Value key = {name, value};
|
||
|
Table_Lookup lookup = table_lookup(&parse->tag_value_to_event_list_table, make_data_struct(&key));
|
||
|
if (lookup.found_match){
|
||
|
u64 val = 0;
|
||
|
table_read(&parse->tag_value_to_event_list_table, lookup, &val);
|
||
|
result = (Log_Event_List*)IntAsPtr(val);
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Log_Event_List*
|
||
|
log_parse__get_or_make_list_tag_value(Log_Parse *parse, Log_Tag *tag){
|
||
|
Log_Event_List *result = 0;
|
||
|
Log_Tag_Name_Value key = {tag->name, tag->value};
|
||
|
Data data_key = make_data_struct(&key);
|
||
|
Table_Lookup lookup = table_lookup(&parse->tag_value_to_event_list_table, data_key);
|
||
|
if (lookup.found_match){
|
||
|
u64 val = 0;
|
||
|
table_read(&parse->tag_value_to_event_list_table, lookup, &val);
|
||
|
result = (Log_Event_List*)IntAsPtr(val);
|
||
|
}
|
||
|
else{
|
||
|
result = push_array_zero(parse->arena, Log_Event_List, 1);
|
||
|
table_insert(&parse->tag_value_to_event_list_table, push_data_copy(parse->arena, data_key),
|
||
|
(u64)PtrAsInt(result));
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Log_Event_List*
|
||
|
log_parse__get_or_make_list_tag_name(Log_Parse *parse, Log_Tag *tag){
|
||
|
Log_Event_List *result = 0;
|
||
|
Table_Lookup lookup = table_lookup(&parse->tag_name_to_event_list_table, tag->name);
|
||
|
if (lookup.found_match){
|
||
|
u64 val = 0;
|
||
|
table_read(&parse->tag_name_to_event_list_table, lookup, &val);
|
||
|
result = (Log_Event_List*)IntAsPtr(val);
|
||
|
}
|
||
|
else{
|
||
|
result = push_array_zero(parse->arena, Log_Event_List, 1);
|
||
|
table_insert(&parse->tag_name_to_event_list_table, tag->name, (u64)PtrAsInt(result));
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Log_Parse
|
||
|
make_log_parse(Arena *arena, String_Const_u8 source){
|
||
|
Log_Parse parse = {};
|
||
|
parse.arena = arena;
|
||
|
parse.string_id_counter = 1;
|
||
|
parse.string_to_id_table = make_table_Data_u64(arena->base_allocator, 500);
|
||
|
parse.id_to_string_table = make_table_u64_Data(arena->base_allocator, 500);
|
||
|
|
||
|
for (;source.size > 0;){
|
||
|
umem end_of_line = string_find_first(source, '\n');
|
||
|
String_Const_u8 line = string_prefix(source, end_of_line);
|
||
|
line = string_skip_chop_whitespace(line);
|
||
|
source = string_skip(source, end_of_line + 1);
|
||
|
|
||
|
String_Const_u8 src_file_name = {};
|
||
|
String_Const_u8 src_line_number = {};
|
||
|
b32 got_source_position = false;
|
||
|
|
||
|
String_Const_u8 whole_line = line;
|
||
|
|
||
|
{
|
||
|
umem colon1 = string_find_first(line, ':');
|
||
|
src_file_name = string_prefix(line, colon1);
|
||
|
line = string_skip(line, colon1 + 1);
|
||
|
|
||
|
umem colon2 = string_find_first(line, ':');
|
||
|
src_line_number = string_prefix(line, colon2);
|
||
|
line = string_skip(line, colon2 + 1);
|
||
|
|
||
|
if (string_is_integer(src_line_number, 10)){
|
||
|
got_source_position = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!got_source_position){
|
||
|
line = whole_line;
|
||
|
|
||
|
umem colon0 = string_find_first(line, ':');
|
||
|
umem colon1 = string_find_first(line, colon0 + 1, ':');
|
||
|
src_file_name = string_prefix(line, colon1);
|
||
|
line = string_skip(line, colon1 + 1);
|
||
|
|
||
|
umem colon2 = string_find_first(line, ':');
|
||
|
src_line_number = string_prefix(line, colon2);
|
||
|
line = string_skip(line, colon2 + 1);
|
||
|
|
||
|
if (string_is_integer(src_line_number, 10)){
|
||
|
got_source_position = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (got_source_position){
|
||
|
umem bracket_open = string_find_first(line, '[');
|
||
|
String_Const_u8 event_name = string_prefix(line, bracket_open);
|
||
|
event_name = string_skip_chop_whitespace(event_name);
|
||
|
line = string_skip(line, bracket_open + 1);
|
||
|
|
||
|
Log_Event *event = log_parse__event(&parse,
|
||
|
src_file_name, src_line_number, event_name);
|
||
|
|
||
|
for (;line.size > 0;){
|
||
|
umem bracket_close = string_find_first(line, ']');
|
||
|
String_Const_u8 tag = string_prefix(line, bracket_close);
|
||
|
line = string_skip(line, bracket_close + 1);
|
||
|
bracket_open = string_find_first(line, '[');
|
||
|
line = string_skip(line, bracket_open + 1);
|
||
|
|
||
|
umem equal_sign = string_find_first(tag, '=');
|
||
|
String_Const_u8 tag_name = string_prefix(tag, equal_sign);
|
||
|
String_Const_u8 tag_contents = string_skip(tag, equal_sign + 1);
|
||
|
|
||
|
log_parse__tag(&parse, event, tag_name, tag_contents);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
// NOTE(allen): fill acceleration structures
|
||
|
|
||
|
parse.tag_value_to_event_list_table = make_table_Data_u64(arena->base_allocator, Thousand(1));
|
||
|
parse.tag_name_to_event_list_table = make_table_u64_u64(arena->base_allocator, 100);
|
||
|
|
||
|
for (Log_Event *event = parse.first_event;
|
||
|
event != 0;
|
||
|
event = event->next){
|
||
|
for (Log_Tag *tag = event->first_tag;
|
||
|
tag != 0;
|
||
|
tag = tag->next){
|
||
|
{
|
||
|
Log_Event_List *list = log_parse__get_or_make_list_tag_value(&parse, tag);
|
||
|
Log_Event_Ptr_Node *node = push_array(arena, Log_Event_Ptr_Node, 1);
|
||
|
sll_queue_push(list->first, list->last, node);
|
||
|
list->count += 1;
|
||
|
node->event = event;
|
||
|
}
|
||
|
{
|
||
|
Log_Event_List *list = log_parse__get_or_make_list_tag_name(&parse, tag);
|
||
|
Log_Event_Ptr_Node *node = push_array(arena, Log_Event_Ptr_Node, 1);
|
||
|
sll_queue_push(list->first, list->last, node);
|
||
|
list->count += 1;
|
||
|
node->event = event;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (Log_Event *event = parse.first_event;
|
||
|
event != 0;
|
||
|
event = event->next){
|
||
|
i32 slot_count = event->tag_count*3/2;
|
||
|
event->tag_name_to_tag_ptr_table = make_table_u64_u64(arena->base_allocator, slot_count);
|
||
|
for (Log_Tag *tag = event->first_tag;
|
||
|
tag != 0;
|
||
|
tag = tag->next){
|
||
|
table_insert(&event->tag_name_to_tag_ptr_table, tag->name, (u64)PtrAsInt(tag));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(parse);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
internal void
|
||
|
log_events_sort_by_tag__inner(Log_Event **events, Log_Sort_Key *keys, i32 first, i32 one_past_last){
|
||
|
if (first + 1 < one_past_last){
|
||
|
i32 pivot_index = one_past_last - 1;
|
||
|
Log_Sort_Key *pivot_key = keys + pivot_index;
|
||
|
i32 j = first;
|
||
|
for (i32 i = first; i < one_past_last; i += 1){
|
||
|
Log_Sort_Key *key = keys + i;
|
||
|
b32 key_is_less_than_pivot_key = false;
|
||
|
if (key->value.kind < pivot_key->value.kind){
|
||
|
key_is_less_than_pivot_key = true;
|
||
|
}
|
||
|
else if (key->value.kind == pivot_key->value.kind){
|
||
|
if (key->value.value < pivot_key->value.value){
|
||
|
key_is_less_than_pivot_key = true;
|
||
|
}
|
||
|
else if (key->value.value == pivot_key->value.value){
|
||
|
if (key->number < pivot_key->number){
|
||
|
key_is_less_than_pivot_key = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (key_is_less_than_pivot_key){
|
||
|
if (j < i){
|
||
|
Swap(Log_Event*, events[i], events[j]);
|
||
|
Swap(Log_Sort_Key, keys[i], keys[j]);
|
||
|
}
|
||
|
j += 1;
|
||
|
}
|
||
|
}
|
||
|
Swap(Log_Event*, events[pivot_index], events[j]);
|
||
|
Swap(Log_Sort_Key, keys[pivot_index], keys[j]);
|
||
|
log_events_sort_by_tag__inner(events, keys, first, j);
|
||
|
log_events_sort_by_tag__inner(events, keys, j + 1, one_past_last);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
log_events_sort_by_tag(Arena *scratch, Log_Event_Ptr_Array array, u64 tag_name){
|
||
|
Temp_Memory temp = begin_temp(scratch);
|
||
|
Log_Sort_Key *keys = push_array(scratch, Log_Sort_Key, array.count);
|
||
|
for (i32 i = 0; i < array.count; i += 1){
|
||
|
Log_Event *event = array.events[i];
|
||
|
Table_Lookup lookup = table_lookup(&event->tag_name_to_tag_ptr_table, tag_name);
|
||
|
if (lookup.found_match){
|
||
|
u64 read_val = 0;
|
||
|
table_read(&event->tag_name_to_tag_ptr_table, lookup, &read_val);
|
||
|
Log_Tag *tag = (Log_Tag*)IntAsPtr(read_val);
|
||
|
keys[i].value = tag->value;
|
||
|
}
|
||
|
else{
|
||
|
keys[i].value.kind = LogTagKind_Null;
|
||
|
keys[i].value.value = 0;
|
||
|
}
|
||
|
keys[i].number = event->event_number;
|
||
|
}
|
||
|
|
||
|
log_events_sort_by_tag__inner(array.events, keys, 0, array.count);
|
||
|
|
||
|
end_temp(temp);
|
||
|
}
|
||
|
|
||
|
internal Log_Event_Ptr_Array
|
||
|
log_event_array_from_list(Arena *arena, Log_Event_List list){
|
||
|
Log_Event_Ptr_Array array = {};
|
||
|
array.count = list.count;
|
||
|
array.events = push_array(arena, Log_Event*, array.count);
|
||
|
i32 counter = 0;
|
||
|
for (Log_Event_Ptr_Node *node = list.first;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
array.events[counter] = node->event;
|
||
|
counter += 1;
|
||
|
}
|
||
|
return(array);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
CUSTOM_COMMAND_SIG(parse_the_log)
|
||
|
CUSTOM_DOC("Tests the log parser")
|
||
|
{
|
||
|
Buffer_ID log_buffer = get_buffer_by_name(app, string_u8_litexpr("*log*"), AccessAll);
|
||
|
Scratch_Block scratch(app);
|
||
|
String_Const_u8 log_text = push_whole_buffer(app, scratch, log_buffer);
|
||
|
Log_Parse parse = make_log_parse(scratch, log_text);
|
||
|
|
||
|
u64 buffer_code = log_parse__string_code(&parse, string_u8_litexpr("buffer"),
|
||
|
LogParse_ExternalString);
|
||
|
u64 thread_code = log_parse__string_code(&parse, string_u8_litexpr("thread"),
|
||
|
LogParse_ExternalString);
|
||
|
|
||
|
Log_Tag_Value value = {};
|
||
|
value.kind = LogTagKind_Integer;
|
||
|
value.value_s = 10;
|
||
|
Log_Event_List *list = log_parse_get_list_tag_value(&parse, buffer_code, value);
|
||
|
|
||
|
Log_Event_Ptr_Array array = log_event_array_from_list(scratch, *list);
|
||
|
log_events_sort_by_tag(scratch, array, thread_code);
|
||
|
|
||
|
for (i32 i = 0; i < array.count; i += 1){
|
||
|
Log_Event *event = array.events[i];
|
||
|
String_Const_u8 src_name = log_parse__get_string(&parse, event->src_file_name);
|
||
|
String_Const_u8 event_name = log_parse__get_string(&parse, event->event_name);
|
||
|
u64 line_number = event->line_number;
|
||
|
|
||
|
List_String_Const_u8 line = {};
|
||
|
string_list_pushf(scratch, &line, "%.*s:%llu: %.*s",
|
||
|
string_expand(src_name), line_number, string_expand(event_name));
|
||
|
|
||
|
for (Log_Tag *node = event->first_tag;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
String_Const_u8 tag_name = log_parse__get_string(&parse, node->name);
|
||
|
|
||
|
switch (node->value.kind){
|
||
|
case LogTagKind_Integer:
|
||
|
{
|
||
|
string_list_pushf(scratch, &line, " [%.*s:%lld]",
|
||
|
string_expand(tag_name), node->value.value_s);
|
||
|
}break;
|
||
|
|
||
|
case LogTagKind_String:
|
||
|
{
|
||
|
String_Const_u8 value = log_parse__get_string(&parse, node->value.value);
|
||
|
string_list_pushf(scratch, &line, " [%.*s:%.*s]",
|
||
|
string_expand(tag_name), string_expand(value));
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string_list_push(scratch, &line, string_u8_litexpr("\n"));
|
||
|
|
||
|
String_Const_u8 line_string = string_list_flatten(scratch, line);
|
||
|
print_message(app, line_string);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// BOTTOM
|
||
|
|