diff --git a/ChangeLog b/ChangeLog index 4700ebdf..adefc677 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +Mon Mar 27 21:44:04 2006 Soeren Sandmann + + * sformat.[ch]: New files containing a simplified and sanitized + version of the state machine and type system from sfile.c. + + * sfile.c: Move the state machine and type management to separate + files. Make the amount of memory used during loading and saving + less obscene. + + * stackstash.c (stack_stash_new_from_root): Make this function + store the root again. + Mon Mar 27 09:55:01 2006 Søren Sandmann * TODO: updates diff --git a/Makefile.am b/Makefile.am index 43e957f5..4e42a5c4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,8 @@ SYSPROF_CORE = \ profile.c \ sfile.h \ sfile.c \ + sformat.h \ + sformat.c \ stackstash.h \ stackstash.c \ module/sysprof-module.h \ diff --git a/TODO b/TODO index 24a18927..448c670d 100644 --- a/TODO +++ b/TODO @@ -128,6 +128,16 @@ Before 1.2: - split out dfa in its own generic class + - Formats should become first-class, stand-alone objects that offers + help with parsing and nothing else. + + ParseContext* format_get_parse_context (format, err); + gboolean parse_context_begin (parse_context, name, err); + gboolean parse_context_end (parse_format, name, err); + + basically, a Format encapsulates a DFA, and a ParseContext encapsulates + the current state. + - make a generic representation of xml files with quarks for strings: struct item { int begin/end/text; diff --git a/profile.c b/profile.c index 17516674..f2bca46a 100644 --- a/profile.c +++ b/profile.c @@ -37,35 +37,44 @@ struct Profile static SFormat * create_format (void) { - SType object_type = 0; - SType node_type = 0; + SFormat *format; + SForward *object_forward; + SForward *node_forward; + + format = sformat_new(); + + object_forward = sformat_declare_forward (format); + node_forward = sformat_declare_forward (format); - return sformat_new ( - sformat_new_record ( - "profile", NULL, - sformat_new_integer ("size"), - sformat_new_pointer ("call_tree", &node_type), - sformat_new_list ( - "objects", NULL, - sformat_new_record ( - "object", &object_type, - sformat_new_string ("name"), - sformat_new_integer ("total"), - sformat_new_integer ("self"), + sformat_set_type ( + format, + sformat_make_record ( + format, "profile", NULL, + sformat_make_integer (format, "size"), + sformat_make_pointer (format, "call_tree", node_forward), + sformat_make_list ( + format, "objects", NULL, + sformat_make_record ( + format, "object", object_forward, + sformat_make_string (format, "name"), + sformat_make_integer (format, "total"), + sformat_make_integer (format, "self"), NULL)), - sformat_new_list ( - "nodes", NULL, - sformat_new_record ( - "node", &node_type, - sformat_new_pointer ("object", &object_type), - sformat_new_pointer ("siblings", &node_type), - sformat_new_pointer ("children", &node_type), - sformat_new_pointer ("parent", &node_type), - sformat_new_integer ("total"), - sformat_new_integer ("self"), - sformat_new_integer ("toplevel"), + sformat_make_list ( + format, "nodes", NULL, + sformat_make_record ( + format, "node", node_forward, + sformat_make_pointer (format, "object", object_forward), + sformat_make_pointer (format, "siblings", node_forward), + sformat_make_pointer (format, "children", node_forward), + sformat_make_pointer (format, "parent", node_forward), + sformat_make_integer (format, "total"), + sformat_make_integer (format, "self"), + sformat_make_integer (format, "toplevel"), NULL)), NULL)); + + return format; } static int @@ -217,7 +226,7 @@ profile_load (const char *filename, GError **err) sformat_free (format); sfile_input_free (input); - + profile->stash = stack_stash_new_from_root (root); return profile; diff --git a/sfile.c b/sfile.c index 7da3c765..ae853dfd 100644 --- a/sfile.c +++ b/sfile.c @@ -27,54 +27,7 @@ #include #endif #include "sfile.h" - -typedef struct State State; -typedef struct Transition Transition; -typedef struct Fragment Fragment; - -struct SFormat -{ - State *begin; - State *end; -}; - -enum -{ - TYPE_UNDEFINED = 0, - TYPE_POINTER, - TYPE_STRING, - TYPE_INTEGER, - TYPE_GENERIC_RECORD, - TYPE_GENERIC_LIST, - TYPE_VOID, - N_BUILTIN_TYPES, -}; - -typedef enum -{ - BEGIN, - VALUE, - END -} TransitionKind; - -struct Transition -{ - SType type; - TransitionKind kind; - State *to; - char *element; /* for begin/end transitions */ - SType target_type; /* for pointer transitions */ -}; - -struct State -{ - GQueue *transitions; -}; - -struct Fragment -{ - Transition *enter, *exit; -}; +#include "sformat.h" static void set_error (GError **err, gint code, const char *format, va_list args) @@ -129,492 +82,47 @@ set_invalid_content_error (GError **err, const char *format, ...) va_end (args); } -static Transition * -transition_new (const char *element, - TransitionKind kind, - SType type, - State *from, - State *to) -{ - Transition *t = g_new (Transition, 1); - - g_assert (element || kind == VALUE); - - t->element = element? g_strdup (element) : NULL; - t->kind = kind; - t->type = type; - t->to = to; - t->target_type = TYPE_UNDEFINED; - - if (from) - g_queue_push_tail (from->transitions, t); - - return t; -} - -static void -transition_free (Transition *transition) -{ - if (transition->element) - g_free (transition->element); - g_free (transition); -} - -static State * -state_new (void) -{ - State *state = g_new (State, 1); - state->transitions = g_queue_new (); - return state; -} - -static void -state_free (State *state) -{ - GList *list; - - for (list = state->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - transition_free (transition); - } - - g_queue_free (state->transitions); - g_free (state); -} - -SFormat * -sformat_new (gpointer f) -{ - SFormat *sformat = g_new0 (SFormat, 1); - Fragment *fragment = f; - - sformat->begin = state_new (); - sformat->end = state_new (); - - g_queue_push_tail (sformat->begin->transitions, fragment->enter); - fragment->exit->to = sformat->end; - - g_free (fragment); - - return sformat; -} - -#if 0 -SFormat * -sformat_new_optional (gpointer f) -{ - SFormat *sformat = g_new0 (SFormat, 1); - Fragment *fragment = f; - - sformat->begin = state_new (); - sformat->end = state_new (); -} -#endif - -static void -add_state (State *state, GHashTable *seen_states, GQueue *todo_list) -{ - if (!g_hash_table_lookup (seen_states, state)) - { - g_hash_table_insert (seen_states, state, state); - g_queue_push_tail (todo_list, state); - } -} - -void -sformat_free (SFormat *format) -{ - GHashTable *seen_states = g_hash_table_new (g_direct_hash, g_direct_equal); - GQueue *todo_list = g_queue_new (); - - add_state (format->begin, seen_states, todo_list); - add_state (format->end, seen_states, todo_list); - - while (!g_queue_is_empty (todo_list)) - { - GList *list; - State *state = g_queue_pop_head (todo_list); - - for (list = state->transitions->head; list != NULL; list = list->next) - { - Transition *transition = list->data; - add_state (transition->to, seen_states, todo_list); - } - - state_free (state); - } - - g_hash_table_destroy (seen_states); - g_queue_free (todo_list); -} - -static GQueue * -fragment_queue (Fragment *fragment1, va_list args) -{ - GQueue *fragments = g_queue_new (); - Fragment *fragment; - - g_queue_push_tail (fragments, fragment1); - - fragment = va_arg (args, Fragment *); - while (fragment) - { - g_queue_push_tail (fragments, fragment); - fragment = va_arg (args, Fragment *); - } - - return fragments; -} - -#if 0 -/* Consider adding unions at some point - * - * To be useful they should probably be anonymous, so that - * the union itself doesn't have a representation in the - * xml file. - * - * API: - * sformat_new_union (gpointer content1, ...); - * - * char *content = begin_get_union (); - * if (strcmp (content, ...) == 0) - * get_pointer (); - * else if (strcmp (content, ...) == 0) - * - * ; - * - * Annoying though, that we then won't have the nice one-to-one - * correspondence between begin()/end() calls and s - * Actually, we will probably have to have asdlfkj - * elements. That will make things a lot easier, and unions are - * still pretty useful if you put big things like lists in them. - * - * We may also consider adding anonymous records. These will - * not be able to have pointers associated with them though - * (because there wouldn't be a natural place - * - * - * Also consider adding the following data types: - * - * Binary blobs of data, stored as base64 perhaps - * floating point values. How do we store those portably - * without losing precision? Gnumeric may know. - * enums, stored as strings - * booleans - */ -gpointer -sformat_new_union (const char *name, - gpointer content1, - ...) -{ - va_list args; - GQueue *fragments; - GList *list; - Fragment *fragment; - Transition *enter, *exit; - State *begin; - State *end; - - va_start (args, content1); - - fragments = fragment_queue (args); - - va_end (args); - - begin = state_new (); - end = state_new (); - - enter = transition_new (name, TRANSITION_BEGIN_UNION, NULL, begin); - exit = transition_new (name, TRANSITION_END_UNION, end, NULL); - - for (list = fragments->head; list; list = list->next) - { - Fragment *fragment = list->data; - - g_queue_push_tail (begin->transitions, fragment->enter); - } - - for (list = fragments->head; list; list = list->next) - { - fragment = list->data; - - fragment->exit->to = end; - - g_free (fragment); - } - - g_queue_free (fragments); - - fragment = g_new (Fragment, 1); - fragment->enter = enter; - fragment->exit = exit; - - return fragment; -} -#endif - -#define RECORD_SHIFT (sizeof (SType) * 8 - 1) -#define LIST_SHIFT (sizeof (SType) * 8 - 2) - -static SType -define_type (SType *type, SType fallback) -{ - static SType type_ids = N_BUILTIN_TYPES; - - if (type) - { - if (*type == 0) - *type = type_ids++; - - return *type; - } - - return fallback; -} - -static gboolean -is_record_type (SType type) -{ - /* FIMXE - not10 */ - return TRUE; -} - -static gboolean -is_list_type (SType type) -{ - /* FIXME - not10 */ - return TRUE; -} - -gpointer -sformat_new_record (const char * name, - SType *type, - gpointer content1, - ...) -{ - va_list args; - GQueue *fragments; - State *begin, *state; - Fragment *fragment; - GList *list; - SType real_type; - - /* Build queue of fragments */ - va_start (args, content1); - - fragments = fragment_queue (content1, args); - - va_end (args); - - /* chain fragments together */ - state = begin = state_new (); - - for (list = fragments->head; list != NULL; list = list->next) - { - fragment = list->data; - - g_queue_push_tail (state->transitions, fragment->enter); - - state = state_new (); - fragment->exit->to = state; - } - - real_type = define_type (type, TYPE_GENERIC_RECORD); - - /* Return resulting fragment */ - fragment = g_new (Fragment, 1); - fragment->enter = transition_new (name, BEGIN, real_type, NULL, begin); - fragment->exit = transition_new (name, END, real_type, state, NULL); - - return fragment; -} - -gpointer -sformat_new_list (const char *name, - SType *type, - gpointer content) -{ - Fragment *m = content; - State *list_state; - Transition *enter, *exit; - SType real_type; - - list_state = state_new (); - - real_type = define_type (type, TYPE_GENERIC_LIST); - - enter = transition_new (name, BEGIN, real_type, NULL, list_state); - exit = transition_new (name, END, real_type, list_state, NULL); - - g_queue_push_tail (list_state->transitions, m->enter); - m->exit->to = list_state; - - m->enter = enter; - m->exit = exit; - - return m; -} - -static gpointer -sformat_new_value (const char *name, - SType type) -{ - Fragment *m = g_new (Fragment, 1); - State *before, *after; - Transition *value; - - before = state_new (); - after = state_new (); - - m->enter = transition_new (name, BEGIN, type, NULL, before); - m->exit = transition_new (name, END, type, after, NULL); - value = transition_new (NULL, VALUE, type, before, after); - - return m; -} - -gpointer -sformat_new_pointer (const char *name, - SType *target_type) -{ - Fragment *fragment = sformat_new_value (name, TYPE_POINTER); - Transition *value; - - /* store the target type in the value transition */ - value = fragment->enter->to->transitions->head->data; - value->target_type = define_type (target_type, TYPE_VOID); - - return fragment; -} - -gpointer -sformat_new_integer (const char *name) -{ - return sformat_new_value (name, TYPE_INTEGER); -} - -gpointer -sformat_new_string (const char *name) -{ - return sformat_new_value (name, TYPE_STRING); -} - -static const State * -sformat_get_start_state (SFormat *format) -{ - return format->begin; -} - -static gboolean -sformat_is_end_state (SFormat *format, const State *state) -{ - return format->end == state; -} - -static const State * -state_transition_check (const State *state, - const char *element, - TransitionKind kind, - SType *type) -{ - GList *list; - - for (list = state->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - if (transition->kind == kind && - strcmp (element, transition->element) == 0) - { - *type = transition->type; - return transition->to; - } - } - - return NULL; -} - -static const State * -state_transition_begin (const State *state, const char *element, SType *type) -{ - return state_transition_check (state, element, BEGIN, type); -} - -static const State * -state_transition_end (const State *state, const char *element, SType *type) -{ - return state_transition_check (state, element, END, type); -} - -static const State * -state_transition_text (const State *state, SType *type, SType *target_type) -{ - GList *list; - - for (list = state->transitions->head; list; list = list->next) - { - Transition *transition = list->data; - - if (transition->kind == VALUE) - { - *type = transition->type; - - if (*type == TYPE_POINTER && target_type) - *target_type = transition->target_type; - - /* There will never be more than one allowed value transition for - * a given state - */ - return transition->to; - } - } - - return NULL; -} - /* reading */ typedef struct BuildContext BuildContext; typedef struct Instruction Instruction; +typedef enum +{ + BEGIN, END, VALUE +} InstructionKind; struct Instruction { - TransitionKind kind; - SType type; + InstructionKind kind; + SType *type; - char *name; union { struct { - gboolean is_list; - gboolean is_record; int n_elements; int id; Instruction *end_instruction; } begin; - + struct { Instruction *begin_instruction; gpointer object; } end; - + struct { - SType target_type; int target_id; Instruction *target_instruction; gpointer target_object; gpointer *location; } pointer; - + struct { int value; } integer; - + struct { char *value; @@ -624,8 +132,8 @@ struct Instruction struct BuildContext { - const State *state; - + SContext *context; + GArray *instructions; }; @@ -634,7 +142,7 @@ is_all_blank (const char *text) { while (g_ascii_isspace (*text)) text++; - + return (*text == '\0'); } @@ -644,9 +152,9 @@ get_number (const char *text, int *number) char *end; int result; gboolean retval; - + result = strtol (text, &end, 10); - + retval = is_all_blank (end); if (retval && number) @@ -663,14 +171,23 @@ struct SFileInput GHashTable *instructions_by_location; }; +gboolean +check_name (Instruction *instr, + const char *name) +{ + const char *type_name = stype_get_name (instr->type); + + return strcmp (name, type_name) == 0; +} + void sfile_begin_get_record (SFileInput *file, const char *name) { Instruction *instruction = file->current_instruction++; - + g_return_if_fail (instruction->kind == BEGIN); - g_return_if_fail (strcmp (instruction->name, name) == 0); - g_return_if_fail (is_record_type (instruction->type)); + g_return_if_fail (check_name (instruction, name)); + g_return_if_fail (stype_is_record (instruction->type)); } int @@ -680,8 +197,8 @@ sfile_begin_get_list (SFileInput *file, Instruction *instruction = file->current_instruction++; g_return_val_if_fail (instruction->kind == BEGIN, 0); - g_return_val_if_fail (strcmp (instruction->name, name) == 0, 0); - g_return_val_if_fail (is_list_type (instruction->type), 0); + g_return_val_if_fail (check_name (instruction, name), 0); + g_return_val_if_fail (stype_is_list (instruction->type), 0); return instruction->u.begin.n_elements; } @@ -692,29 +209,29 @@ sfile_get_pointer (SFileInput *file, gpointer *location) { Instruction *instruction; - - instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_POINTER && - strcmp (instruction->name, name) == 0); instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_POINTER); + g_return_if_fail (stype_is_pointer (instruction->type) && + check_name (instruction, name)); + + instruction = file->current_instruction++; + g_return_if_fail (stype_is_pointer (instruction->type)); instruction->u.pointer.location = location; - + *location = (gpointer) 0xFedeAbe; - + if (location) { if (g_hash_table_lookup (file->instructions_by_location, location)) g_warning ("Reading into the same location twice\n"); - + g_hash_table_insert (file->instructions_by_location, location, instruction); } - + instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_POINTER && - strcmp (instruction->name, name) == 0); + g_return_if_fail (stype_is_pointer (instruction->type) && + check_name (instruction, name)); } @@ -726,18 +243,18 @@ sfile_get_integer (SFileInput *file, Instruction *instruction; instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_INTEGER && - strcmp (instruction->name, name) == 0); + g_return_if_fail (stype_is_integer (instruction->type) && + check_name (instruction, name)); instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_INTEGER); + g_return_if_fail (stype_is_integer (instruction->type)); if (integer) *integer = instruction->u.integer.value; - + instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_INTEGER && - strcmp (instruction->name, name) == 0); + g_return_if_fail (stype_is_integer (instruction->type) && + check_name (instruction, name)); } void @@ -746,51 +263,51 @@ sfile_get_string (SFileInput *file, char **string) { Instruction *instruction; - - instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_STRING && - strcmp (instruction->name, name) == 0); instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_STRING); + g_return_if_fail (stype_is_string (instruction->type) && + check_name (instruction, name)); + + instruction = file->current_instruction++; + g_return_if_fail (stype_is_string (instruction->type)); if (string) *string = g_strdup (instruction->u.string.value); - + instruction = file->current_instruction++; - g_return_if_fail (instruction->type == TYPE_STRING && - strcmp (instruction->name, name) == 0); + g_return_if_fail (stype_is_string (instruction->type) && + check_name (instruction, name)); } static void hook_up_pointers (SFileInput *file) { int i; - + #if 0 g_print ("emfle\n"); #endif for (i = 0; i < file->n_instructions; ++i) { Instruction *instruction = &(file->instructions[i]); - + if (instruction->kind == VALUE && - instruction->type == TYPE_POINTER) + stype_is_pointer (instruction->type)) { gpointer target_object; Instruction *target_instruction; - + target_instruction = instruction->u.pointer.target_instruction; - + if (target_instruction) target_object = target_instruction->u.begin.end_instruction->u.end.object; else target_object = NULL; - + #if 0 g_print ("target object: %p\n", target_object); #endif - + *(instruction->u.pointer.location) = target_object; } } @@ -804,10 +321,10 @@ sfile_end_get (SFileInput *file, Instruction *instruction = file->current_instruction++; g_return_if_fail (instruction->kind == END); - g_return_if_fail (strcmp (instruction->name, name) == 0); - + g_return_if_fail (check_name (instruction, name)); + instruction->u.end.object = object; - + if (file->current_instruction == file->instructions + file->n_instructions) hook_up_pointers (file); } @@ -825,13 +342,13 @@ get_id (const char **names, const char **values, GError **err) set_unknown_attribute_error (err, "Unknown attribute: %s", names[i]); return -1; } - + if (id_string) { set_invalid_content_error (err, "Attribute 'id' defined twice"); return -1; } - + id_string = values[i]; } @@ -857,21 +374,20 @@ handle_begin_element (GMarkupParseContext *parse_context, { BuildContext *build = user_data; Instruction instruction; - + instruction.u.begin.id = get_id (attribute_names, attribute_values, err); - + if (instruction.u.begin.id == -1) return; - - build->state = state_transition_begin (build->state, element_name, &instruction.type); - if (!build->state) + + instruction.type = scontext_begin (build->context, element_name); + if (!instruction.type) { set_unknown_element_error (err, "<%s> unexpected here", element_name); return; } - + /* FIXME - not10: is there really a reason to add begin/end instructions for values? */ - instruction.name = g_strdup (element_name); instruction.kind = BEGIN; g_array_append_val (build->instructions, instruction); } @@ -885,16 +401,15 @@ handle_end_element (GMarkupParseContext *context, BuildContext *build = user_data; Instruction instruction; - build->state = state_transition_end (build->state, element_name, &instruction.type); - if (!build->state) + instruction.type = scontext_end (build->context, element_name); + if (!instruction.type) { set_unknown_element_error (err, " unexpected here", element_name); return; } - - instruction.name = g_strdup (element_name); + instruction.kind = END; - + g_array_append_val (build->instructions, instruction); } @@ -902,13 +417,13 @@ static gboolean decode_text (const char *text, char **decoded) { int length = strlen (text); - + if (length < 2) return FALSE; - + if (text[0] != '\"' || text[length - 1] != '\"') return FALSE; - + if (decoded) *decoded = g_strndup (text + 1, length - 2); @@ -920,7 +435,7 @@ skip_whitespace (const char *text) { while (g_ascii_isspace (*text)) text++; - + return text; } @@ -933,8 +448,7 @@ handle_text (GMarkupParseContext *context, { BuildContext *build = user_data; Instruction instruction; - SType target_type; - + if (*text == '\0') return; @@ -942,48 +456,45 @@ handle_text (GMarkupParseContext *context, if (*text == '\0') return; - build->state = state_transition_text (build->state, &instruction.type, &target_type); - if (!build->state) + instruction.type = scontext_text (build->context); + if (!instruction.type) { + /* FIXME: what are line and ch used for here? */ int line, ch; g_markup_parse_context_get_position (context, &line, &ch); set_invalid_content_error (err, "Unexpected text data"); return; } - - instruction.name = NULL; + instruction.kind = VALUE; - switch (instruction.type) + if (stype_is_pointer (instruction.type)) { - case TYPE_POINTER: - instruction.u.pointer.target_type = target_type; if (!get_number (text, &instruction.u.pointer.target_id)) { set_invalid_content_error (err, "Contents '%s' of pointer element is not a number", text); return; } - break; - - case TYPE_INTEGER: + } + else if (stype_is_integer (instruction.type)) + { if (!get_number (text, &instruction.u.integer.value)) { set_invalid_content_error (err, "Contents '%s' of integer element not a number", text); return; } - break; - - case TYPE_STRING: + } + else if (stype_is_string (instruction.type)) + { if (!decode_text (text, &instruction.u.string.value)) { set_invalid_content_error (err, "Contents '%s' of text element is illformed", text); return; } - break; - - default: - g_assert_not_reached(); - break; + } + else + { + g_assert_not_reached(); } g_array_append_val (build->instructions, instruction); @@ -993,15 +504,12 @@ static void free_instructions (Instruction *instructions, int n_instructions) { int i; - + for (i = 0; i < n_instructions; ++i) { Instruction *instruction = &(instructions[i]); - - if (instruction->name) - g_free (instruction->name); - - if (instruction->kind == VALUE && instruction->type == TYPE_STRING) + + if (instruction->kind == VALUE && stype_is_string (instruction->type)) g_free (instruction->u.string.value); } @@ -1017,11 +525,11 @@ process_instruction_pairs (Instruction *first) { Instruction *instruction; int n_elements; - + g_assert (first->kind == BEGIN); - + instruction = first + 1; - + n_elements = 0; while (instruction->kind != END) { @@ -1035,25 +543,25 @@ process_instruction_pairs (Instruction *first) { instruction++; } - + n_elements++; } - + first->u.begin.n_elements = n_elements; first->u.begin.end_instruction = instruction; - + instruction->u.end.begin_instruction = first; - + return instruction + 1; } - + static gboolean post_process_read_instructions (Instruction *instructions, int n_instructions, GError **err) { gboolean retval = TRUE; GHashTable *instructions_by_id; int i; - + /* count list instructions, check pointers */ process_instruction_pairs (instructions); @@ -1062,34 +570,34 @@ post_process_read_instructions (Instruction *instructions, int n_instructions, G for (i = 0; i < n_instructions; ++i) { Instruction *instruction = &(instructions[i]); - + if (instruction->kind == BEGIN) { int id = instruction->u.begin.id; - + if (id) g_hash_table_insert (instructions_by_id, GINT_TO_POINTER (id), instruction); } } - + /* Make pointer instructions point to the corresponding element */ for (i = 0; i < n_instructions; ++i) { Instruction *instruction = &(instructions[i]); - + if (instruction->kind == VALUE && - instruction->type == TYPE_POINTER) + stype_is_pointer (instruction->type)) { int target_id = instruction->u.pointer.target_id; - + if (target_id) { Instruction *target = g_hash_table_lookup (instructions_by_id, GINT_TO_POINTER (target_id)); - + if (target) { - if (instruction->u.pointer.target_type == target->type) + if (stype_get_target_type (instruction->type) == target->type) { instruction->u.pointer.target_instruction = target; } @@ -1117,7 +625,7 @@ post_process_read_instructions (Instruction *instructions, int n_instructions, G } g_hash_table_destroy (instructions_by_id); - + return retval; } @@ -1135,7 +643,7 @@ build_instructions (const char *contents, SFormat *format, int *n_instructions, NULL, /* error */ }; - build.state = sformat_get_start_state (format); + build.context = scontext_new (format); build.instructions = g_array_new (TRUE, TRUE, sizeof (Instruction)); parse_context = g_markup_parse_context_new (&parser, 0, &build, NULL); @@ -1145,23 +653,26 @@ build_instructions (const char *contents, SFormat *format, int *n_instructions, free_instructions ((Instruction *)build.instructions->data, build.instructions->len); return NULL; } - - if (!sformat_is_end_state (format, build.state)) + + if (!scontext_is_finished (build.context)) { set_invalid_content_error (err, "Premature end of file\n"); - + free_instructions ((Instruction *)build.instructions->data, build.instructions->len); return NULL; } - + if (!post_process_read_instructions ((Instruction *)build.instructions->data, build.instructions->len, err)) { free_instructions ((Instruction *)build.instructions->data, build.instructions->len); return NULL; } - + *n_instructions = build.instructions->len; + + g_print ("n instructions: %d\n", *n_instructions); + return (Instruction *)g_array_free (build.instructions, FALSE); } @@ -1189,7 +700,7 @@ sfile_load (const char *filename, } g_free (contents); - + input->current_instruction = input->instructions; input->instructions_by_location = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -1202,7 +713,7 @@ struct SFileOutput SFormat *format; GArray *instructions; GHashTable *objects; - const State *state; + SContext *context; }; SFileOutput * @@ -1212,9 +723,9 @@ sfile_output_new (SFormat *format) output->format = format; output->instructions = g_array_new (TRUE, TRUE, sizeof (Instruction)); - output->state = sformat_get_start_state (format); + output->context = scontext_new (format); output->objects = g_hash_table_new (g_direct_hash, g_direct_equal); - + return output; } @@ -1224,14 +735,13 @@ sfile_begin_add_record (SFileOutput *file, { Instruction instruction; - file->state = state_transition_begin (file->state, name, &instruction.type); - - g_return_if_fail (file->state); - g_return_if_fail (is_record_type (instruction.type)); + instruction.type = scontext_begin (file->context, name); + + g_return_if_fail (instruction.type); + g_return_if_fail (stype_is_record (instruction.type)); instruction.kind = BEGIN; - instruction.name = g_strdup (name); - + g_array_append_val (file->instructions, instruction); } @@ -1240,15 +750,14 @@ sfile_begin_add_list (SFileOutput *file, const char *name) { Instruction instruction; - - file->state = state_transition_begin (file->state, name, &instruction.type); - - g_return_if_fail (file->state); - g_return_if_fail (is_list_type (instruction.type)); - + + instruction.type = scontext_begin (file->context, name); + + g_return_if_fail (instruction.type); + g_return_if_fail (stype_is_list (instruction.type)); + instruction.kind = BEGIN; - instruction.name = g_strdup (name); - + g_array_append_val (file->instructions, instruction); } @@ -1258,46 +767,49 @@ sfile_end_add (SFileOutput *file, gpointer object) { Instruction instruction; - + if (object && g_hash_table_lookup (file->objects, object)) { g_warning ("Adding the same object (%p) twice", object); return; } - file->state = state_transition_end (file->state, name, &instruction.type); - - if (!file->state) + instruction.type = scontext_end (file->context, name); + + if (!instruction.type) { g_warning ("invalid call of sfile_end_add()"); return; } - + instruction.kind = END; - instruction.name = g_strdup (name); instruction.u.end.object = object; - + g_array_append_val (file->instructions, instruction); - + if (object) g_hash_table_insert (file->objects, object, object); } -static void +static SType * sfile_check_value (SFileOutput *file, - const char *name, - SType type) + const char *name) { - SType tmp_type; + SType *tmp_type; - file->state = state_transition_begin (file->state, name, &tmp_type); - g_return_if_fail (file->state && tmp_type == type); - - file->state = state_transition_text (file->state, &type, NULL); - g_return_if_fail (file->state && tmp_type == type); + tmp_type = scontext_begin (file->context, name); + if (!tmp_type) + return NULL; - file->state = state_transition_end (file->state, name, &type); - g_return_if_fail (file->state && tmp_type == type); + tmp_type = scontext_text (file->context); + if (!tmp_type) + return NULL; + + tmp_type = scontext_end (file->context, name); + if (!tmp_type) + return NULL; + + return tmp_type; } void @@ -1306,16 +818,18 @@ sfile_add_string (SFileOutput *file, const char *string) { Instruction instruction; - + g_return_if_fail (g_utf8_validate (string, -1, NULL)); - sfile_check_value (file, name, TYPE_STRING); - + instruction.type = sfile_check_value (file, name); + if (!instruction.type || !stype_is_string (instruction.type)) + { + g_warning ("Invalid call to sfile_add_string()"); + return; + } instruction.kind = VALUE; - instruction.type = TYPE_STRING; - instruction.name = g_strdup (name); instruction.u.string.value = g_strdup (string); - + g_array_append_val (file->instructions, instruction); } @@ -1326,13 +840,15 @@ sfile_add_integer (SFileOutput *file, { Instruction instruction; - sfile_check_value (file, name, TYPE_INTEGER); - + instruction.type = sfile_check_value (file, name); + if (!instruction.type || !stype_is_integer (instruction.type)) + { + g_warning ("Invalid call to sfile_add_integer()"); + return; + } instruction.kind = VALUE; - instruction.type = TYPE_INTEGER; - instruction.name = g_strdup (name); instruction.u.integer.value = integer; - + g_array_append_val (file->instructions, instruction); } @@ -1343,13 +859,15 @@ sfile_add_pointer (SFileOutput *file, { Instruction instruction; - sfile_check_value (file, name, TYPE_POINTER); - + instruction.type = sfile_check_value (file, name); + if (!instruction.type || !stype_is_pointer (instruction.type)) + { + g_warning ("Invalid call to sfile_add_pointer()"); + return; + } instruction.kind = VALUE; - instruction.type = TYPE_POINTER; - instruction.name = g_strdup (name); instruction.u.pointer.target_object = pointer; - + g_array_append_val (file->instructions, instruction); } @@ -1363,15 +881,15 @@ post_process_write_instructions (SFileOutput *sfile) GHashTable *instructions_by_object; process_instruction_pairs (instructions); - + /* Set all id's to -1 and create map from objects to instructions */ - + instructions_by_object = g_hash_table_new (g_direct_hash, g_direct_equal); for (i = 0; i < n_instructions; ++i) { Instruction *instruction = &(instructions[i]); - + if (instruction->kind == BEGIN) { instruction->u.begin.id = -1; @@ -1382,34 +900,34 @@ post_process_write_instructions (SFileOutput *sfile) instruction->u.end.object, instruction); } } - + /* Assign an id to all pointed-to instructions */ id = 1; for (i = 0; i < n_instructions; ++i) { Instruction *instruction = &(instructions[i]); - - if (instruction->type == TYPE_POINTER) + + if (stype_is_pointer (instruction->type)) { if (instruction->u.pointer.target_object) { Instruction *target; - + target = g_hash_table_lookup (instructions_by_object, instruction->u.pointer.target_object); - + if (!target) { g_warning ("pointer has unknown target\n"); return; } - + g_assert (target->kind == END); - + if (target->u.end.begin_instruction->u.begin.id == -1) target->u.end.begin_instruction->u.begin.id = id++; - + instruction->u.pointer.target_id = target->u.end.begin_instruction->u.begin.id; } @@ -1426,7 +944,7 @@ static void add_indent (GString *output, int indent) { int i; - + for (i = 0; i < indent; ++i) g_string_append_c (output, ' '); } @@ -1451,7 +969,7 @@ static void add_begin_tag (GString *output, int indent, const char *name, int id) { add_indent (output, indent); - + if (id != -1) g_string_append_printf (output, "<%s id=\"%d\">", name, id); else @@ -1495,7 +1013,7 @@ disaster (int status) case BZ_OUTBUFF_FULL: error = "BZ_OUTBUFF_FULL"; break; - + default: error = "Unknown error"; break; @@ -1536,12 +1054,18 @@ bz2_compress (const guchar *input, int input_length, *output = compressed_data; else g_free (compressed_data); - + if (output_length) *output_length = compressed_size; } #endif +static const char * +get_name (Instruction *inst) +{ + return stype_get_name (inst->type); +} + gboolean sfile_output_save (SFileOutput *sfile, const char *filename, @@ -1556,11 +1080,11 @@ sfile_output_save (SFileOutput *sfile, guchar *compressed; size_t compressed_size; #endif - + g_return_val_if_fail (sfile != NULL, FALSE); - + instructions = (Instruction *)sfile->instructions->data; - + post_process_write_instructions (sfile); indent = 0; @@ -1568,44 +1092,43 @@ sfile_output_save (SFileOutput *sfile, for (i = 0; i < sfile->instructions->len; ++i) { Instruction *instruction = &(instructions[i]); - + switch (instruction->kind) { case BEGIN: - add_begin_tag (output, indent, instruction->name, + add_begin_tag (output, indent, get_name (instruction), instruction->u.begin.id); add_nl (output); indent += 4; break; - + case END: indent -= 4; - add_end_tag (output, indent, instruction->name); + add_end_tag (output, indent, get_name (instruction)); add_nl (output); break; - + case VALUE: - add_begin_tag (output, indent, instruction->name, -1); - switch (instruction->type) + add_begin_tag (output, indent, get_name (instruction), -1); + + if (stype_is_integer (instruction->type)) { - case TYPE_INTEGER: add_integer (output, instruction->u.integer.value); - break; - - case TYPE_POINTER: + } + else if (stype_is_pointer (instruction->type)) + { add_integer (output, instruction->u.pointer.target_id); - break; - - case TYPE_STRING: + } + else if (stype_is_string (instruction->type)) + { add_string (output, instruction->u.string.value); - break; } - add_end_tag (output, 0, instruction->name); + add_end_tag (output, 0, get_name (instruction)); add_nl (output); break; } } - + #if 0 /* FIXME - not10: bz2 compressing the output is probably * interesting at some point. For now just make sure @@ -1613,14 +1136,14 @@ sfile_output_save (SFileOutput *sfile, */ bz2_compress (output->str, output->len, &compressed, &compressed_size); - + g_free (compressed); #endif retval = file_replace (filename, output->str, - 1, err); g_string_free (output, TRUE); - + return retval; } @@ -1629,9 +1152,9 @@ void sfile_input_free (SFileInput *file) { free_instructions (file->instructions, file->n_instructions); - + g_hash_table_destroy (file->instructions_by_location); - + g_free (file); } @@ -1640,12 +1163,12 @@ sfile_output_free (SFileOutput *sfile) { Instruction *instructions; int n_instructions; - + n_instructions = sfile->instructions->len; instructions = (Instruction *)g_array_free (sfile->instructions, FALSE); - + free_instructions (instructions, n_instructions); - + g_hash_table_destroy (sfile->objects); g_free (sfile); } @@ -1675,28 +1198,28 @@ rename_file (const char *old_name, const char *new_name, GError **err) { - errno = 0; - if (g_rename (old_name, new_name) == -1) + errno = 0; + if (g_rename (old_name, new_name) == -1) { - int save_errno = errno; - gchar *display_old_name = g_filename_display_name (old_name); - gchar *display_new_name = g_filename_display_name (new_name); - - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (save_errno), - "Failed to rename file '%s' to '%s': g_rename() failed: %s", - display_old_name, - display_new_name, - g_strerror (save_errno)); - - g_free (display_old_name); - g_free (display_new_name); - - return FALSE; + int save_errno = errno; + gchar *display_old_name = g_filename_display_name (old_name); + gchar *display_new_name = g_filename_display_name (new_name); + + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to rename file '%s' to '%s': g_rename() failed: %s", + display_old_name, + display_new_name, + g_strerror (save_errno)); + + g_free (display_old_name); + g_free (display_new_name); + + return FALSE; } - - return TRUE; + + return TRUE; } static gboolean @@ -1704,89 +1227,89 @@ set_umask_permissions (int fd, GError **err) { #ifdef G_OS_WIN32 - - return TRUE; - + + return TRUE; + #else - - /* All of this function is just to work around the fact that - * there is no way to get the umask without changing it. - * - * We can't just change-and-reset the umask because that would - * lead to a race condition if another thread tried to change - * the umask in between the getting and the setting of the umask. - * So we have to do the whole thing in a child process. - */ - - pid_t pid = fork (); - - if (pid == -1) + + /* All of this function is just to work around the fact that + * there is no way to get the umask without changing it. + * + * We can't just change-and-reset the umask because that would + * lead to a race condition if another thread tried to change + * the umask in between the getting and the setting of the umask. + * So we have to do the whole thing in a child process. + */ + + pid_t pid = fork (); + + if (pid == -1) { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (errno), - "Could not change file mode: fork() failed: %s", - g_strerror (errno)); - - return FALSE; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Could not change file mode: fork() failed: %s", + g_strerror (errno)); + + return FALSE; } - else if (pid == 0) + else if (pid == 0) { - /* child */ - mode_t mask = umask (0666); - - errno = 0; - if (fchmod (fd, 0666 & ~mask) == -1) - _exit (errno); - else - _exit (0); - - return TRUE; /* To quiet gcc */ + /* child */ + mode_t mask = umask (0666); + + errno = 0; + if (fchmod (fd, 0666 & ~mask) == -1) + _exit (errno); + else + _exit (0); + + return TRUE; /* To quiet gcc */ } - else + else { - /* parent */ - int status; - - waitpid (pid, &status, 0); - - if (WIFEXITED (status)) + /* parent */ + int status; + + waitpid (pid, &status, 0); + + if (WIFEXITED (status)) { - int chmod_errno = WEXITSTATUS (status); - - if (chmod_errno == 0) + int chmod_errno = WEXITSTATUS (status); + + if (chmod_errno == 0) { - return TRUE; + return TRUE; } - else + else { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (chmod_errno), - "Could not change file mode: chmod() failed: %s", - g_strerror (chmod_errno)); - - return FALSE; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (chmod_errno), + "Could not change file mode: chmod() failed: %s", + g_strerror (chmod_errno)); + + return FALSE; } } - else if (WIFSIGNALED (status)) + else if (WIFSIGNALED (status)) { - g_set_error (err, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - "Could not change file mode: Child terminated by signal: %s", - g_strsignal (WTERMSIG (status))); - - return FALSE; + g_set_error (err, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Could not change file mode: Child terminated by signal: %s", + g_strsignal (WTERMSIG (status))); + + return FALSE; } - else + else { - /* This shouldn't happen */ - g_set_error (err, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - "Could not change file mode: Child terminated abnormally"); - return FALSE; + /* This shouldn't happen */ + g_set_error (err, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Could not change file mode: Child terminated abnormally"); + return FALSE; } } #endif @@ -1798,104 +1321,104 @@ write_to_temp_file (const gchar *contents, const gchar *template, GError **err) { - gchar *tmp_name; - gchar *display_name; - gchar *retval; - FILE *file; - gint fd; - int save_errno; - - retval = NULL; - - tmp_name = g_strdup_printf ("%s.XXXXXX", template); - - errno = 0; - fd = g_mkstemp (tmp_name); - save_errno = errno; - display_name = g_filename_display_name (tmp_name); - - if (fd == -1) + gchar *tmp_name; + gchar *display_name; + gchar *retval; + FILE *file; + gint fd; + int save_errno; + + retval = NULL; + + tmp_name = g_strdup_printf ("%s.XXXXXX", template); + + errno = 0; + fd = g_mkstemp (tmp_name); + save_errno = errno; + display_name = g_filename_display_name (tmp_name); + + if (fd == -1) { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (save_errno), - "Failed to create file '%s': %s", - display_name, g_strerror (save_errno)); - - goto out; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + "Failed to create file '%s': %s", + display_name, g_strerror (save_errno)); + + goto out; } - - if (!set_umask_permissions (fd, err)) + + if (!set_umask_permissions (fd, err)) { - close (fd); - g_unlink (tmp_name); - - goto out; + close (fd); + g_unlink (tmp_name); + + goto out; } - - errno = 0; - file = fdopen (fd, "wb"); - if (!file) + + errno = 0; + file = fdopen (fd, "wb"); + if (!file) { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (errno), - "Failed to open file '%s' for writing: fdopen() failed: %s", - display_name, - g_strerror (errno)); - - close (fd); - g_unlink (tmp_name); - - goto out; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to open file '%s' for writing: fdopen() failed: %s", + display_name, + g_strerror (errno)); + + close (fd); + g_unlink (tmp_name); + + goto out; } - - if (length > 0) + + if (length > 0) { - size_t n_written; - - errno = 0; - - n_written = fwrite (contents, 1, length, file); - - if (n_written < length) + size_t n_written; + + errno = 0; + + n_written = fwrite (contents, 1, length, file); + + if (n_written < length) { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (errno), - "Failed to write file '%s': fwrite() failed: %s", - display_name, - g_strerror (errno)); - - fclose (file); - g_unlink (tmp_name); - - goto out; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to write file '%s': fwrite() failed: %s", + display_name, + g_strerror (errno)); + + fclose (file); + g_unlink (tmp_name); + + goto out; } } - - errno = 0; - if (fclose (file) == EOF) + + errno = 0; + if (fclose (file) == EOF) { - g_set_error (err, - G_FILE_ERROR, - g_file_error_from_errno (errno), - "Failed to close file '%s': fclose() failed: %s", - display_name, - g_strerror (errno)); - - g_unlink (tmp_name); - - goto out; + g_set_error (err, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to close file '%s': fclose() failed: %s", + display_name, + g_strerror (errno)); + + g_unlink (tmp_name); + + goto out; } - - retval = g_strdup (tmp_name); - - out: - g_free (tmp_name); - g_free (display_name); - - return retval; + + retval = g_strdup (tmp_name); + +out: + g_free (tmp_name); + g_free (display_name); + + return retval; } static gboolean @@ -1904,82 +1427,82 @@ file_replace (const gchar *filename, gssize length, GError **error) { - gchar *tmp_filename; - gboolean retval; - GError *rename_error = NULL; - - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_return_val_if_fail (contents != NULL || length == 0, FALSE); - g_return_val_if_fail (length >= -1, FALSE); - - if (length == -1) - length = strlen (contents); - - tmp_filename = write_to_temp_file (contents, length, filename, error); - - if (!tmp_filename) + gchar *tmp_filename; + gboolean retval; + GError *rename_error = NULL; + + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (contents != NULL || length == 0, FALSE); + g_return_val_if_fail (length >= -1, FALSE); + + if (length == -1) + length = strlen (contents); + + tmp_filename = write_to_temp_file (contents, length, filename, error); + + if (!tmp_filename) { - retval = FALSE; - goto out; + retval = FALSE; + goto out; } - - if (!rename_file (tmp_filename, filename, &rename_error)) + + if (!rename_file (tmp_filename, filename, &rename_error)) { #ifndef G_OS_WIN32 - - g_unlink (tmp_filename); - g_propagate_error (error, rename_error); - retval = FALSE; - goto out; - + + g_unlink (tmp_filename); + g_propagate_error (error, rename_error); + retval = FALSE; + goto out; + #else /* G_OS_WIN32 */ - + /* Renaming failed, but on Windows this may just mean * the file already exists. So if the target file * exists, try deleting it and do the rename again. */ - if (!g_file_test (filename, G_FILE_TEST_EXISTS)) + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { - g_unlink (tmp_filename); - g_propagate_error (error, rename_error); - retval = FALSE; - goto out; + g_unlink (tmp_filename); + g_propagate_error (error, rename_error); + retval = FALSE; + goto out; } - - g_error_free (rename_error); - - if (g_unlink (filename) == -1) + + g_error_free (rename_error); + + if (g_unlink (filename) == -1) { - gchar *display_filename = g_filename_display_name (filename); - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errno), - "Existing file '%s' could not be removed: g_unlink() failed: %s", - display_filename, - g_strerror (errno)); - - g_free (display_filename); - g_unlink (tmp_filename); - retval = FALSE; - goto out; + gchar *display_filename = g_filename_display_name (filename); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Existing file '%s' could not be removed: g_unlink() failed: %s", + display_filename, + g_strerror (errno)); + + g_free (display_filename); + g_unlink (tmp_filename); + retval = FALSE; + goto out; } - - if (!rename_file (tmp_filename, filename, error)) + + if (!rename_file (tmp_filename, filename, error)) { - g_unlink (tmp_filename); - retval = FALSE; - goto out; + g_unlink (tmp_filename); + retval = FALSE; + goto out; } - + #endif } - - retval = TRUE; - - out: - g_free (tmp_filename); - return retval; + + retval = TRUE; + +out: + g_free (tmp_filename); + return retval; } diff --git a/sfile.h b/sfile.h index 00f0a0c8..abee28b3 100644 --- a/sfile.h +++ b/sfile.h @@ -17,10 +17,10 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -typedef struct SFormat SFormat; +#include "sformat.h" + typedef struct SFileInput SFileInput; typedef struct SFileOutput SFileOutput; -typedef guint SType; #if 0 @@ -90,19 +90,6 @@ SerializerFormat *serializer_make_pointer (Serializer *serialiser, */ /* - Describing Types - */ -SFormat *sformat_new (gpointer f); -gpointer sformat_new_record (const char *name, - SType *type, - gpointer content, - ...); -gpointer sformat_new_list (const char *name, - SType *type, - gpointer content); -gpointer sformat_new_pointer (const char *name, - SType *target_type); -gpointer sformat_new_integer (const char *name); -gpointer sformat_new_string (const char *name); -void sformat_free (SFormat *format); /* - Reading - */ diff --git a/sformat.c b/sformat.c new file mode 100644 index 00000000..a98dab0f --- /dev/null +++ b/sformat.c @@ -0,0 +1,639 @@ +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, 2006, Soeren Sandmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "sformat.h" + +typedef struct State State; +typedef struct Transition Transition; +typedef struct Fragment Fragment; + +/* + * Format + */ +struct SFormat +{ + State * begin; + State * end; + + GQueue * types; + GQueue * transitions; + GQueue * states; +}; + +/* + * Type + */ +typedef enum +{ + TYPE_POINTER, + TYPE_STRING, + TYPE_INTEGER, + TYPE_RECORD, + TYPE_LIST, + TYPE_FORWARD +} TypeKind; + +struct SType +{ + TypeKind kind; + char *name; + Transition *enter, *exit; + SType *target; /* If kind is TYPE_POINTER */ +}; + +static void type_free (SType *type); + + +gboolean +stype_is_record (SType *type) +{ + return type->kind == TYPE_RECORD; +} + +gboolean +stype_is_list (SType *type) +{ + return type->kind == TYPE_LIST; +} + +gboolean +stype_is_pointer (SType *type) +{ + return type->kind == TYPE_POINTER; +} + +gboolean +stype_is_integer (SType *type) +{ + return type->kind == TYPE_INTEGER; +} + +gboolean +stype_is_string (SType *type) +{ + return type->kind == TYPE_STRING; +} + +SType * +stype_get_target_type (SType *type) +{ + g_return_val_if_fail (stype_is_pointer (type), NULL); + + return type->target; +} + +const char * +stype_get_name (SType *type) +{ + return type->name; +} + +/* + * Transition + */ +typedef enum +{ + BEGIN, + VALUE, + END +} TransitionKind; + +struct Transition +{ + SType * type; + TransitionKind kind; + State * to; +}; + +static Transition *transition_new (SFormat *format, + TransitionKind kind, + SType *type, + State *from, + State *to); +static void transition_free (Transition *transition); +static void transition_set_to_state (Transition *transition, + State *to_state); + +/* + * State + */ + +struct State +{ + GQueue *transitions; +}; + +static State *state_new (SFormat *format); +static void state_add_transition (State *state, + Transition *transition); +static void state_free (State *state); + +/* + * Format + */ +SFormat * +sformat_new (void) +{ + /* FIXME: should probably be refcounted, and an SContext + * should have a ref on the format + */ + SFormat *format = g_new0 (SFormat, 1); + + format->begin = NULL; + format->end = NULL; + + format->types = g_queue_new (); + format->states = g_queue_new (); + format->transitions = g_queue_new (); + + return format; +} + +void +sformat_free (SFormat *format) +{ + GList *list; + + for (list = format->types->head; list; list = list->next) + { + SType *type = list->data; + + type_free (type); + } + + for (list = format->states->head; list; list = list->next) + { + State *state = list->data; + + state_free (state); + } + + for (list = format->transitions->head; list; list = list->next) + { + Transition *transition = list->data; + + transition_free (transition); + } + + g_free (format); +} + +void +sformat_set_type (SFormat *format, + SType *type) +{ + format->begin = state_new (format); + format->end = state_new (format); + + state_add_transition (format->begin, type->enter); + transition_set_to_state (type->exit, format->end); + + /* Fix up pointer types */ +} + +/* + * Type + */ +static SType * +type_new (SFormat *format, + TypeKind kind, + const char *name) +{ + SType *type = g_new0 (SType, 1); + + type->kind = kind; + type->name = name? g_strdup (name) : NULL; + type->enter = NULL; + type->exit = NULL; + type->target = NULL; + + g_queue_push_tail (format->types, type); + + return type; +} + +static void +type_free (SType *type) +{ + g_free (type->name); + g_free (type); +} + +static GQueue * +type_queue (SType *content1, + va_list args) +{ + GQueue *types = g_queue_new (); + SType *type; + + g_queue_push_tail (types, content1); + + type = va_arg (args, SType *); + while (type) + { + g_queue_push_tail (types, type); + type = va_arg (args, SType *); + } + + return types; +} + +SForward * +sformat_declare_forward (SFormat *format) +{ + SType *type = type_new (format, TYPE_FORWARD, NULL); + + return (SForward *)type; +} + + +SType * +sformat_make_record (SFormat *format, + const char *name, + SForward *forward, + SType *content, + ...) +{ + SType *type; + va_list args; + GQueue *types; + GList *list; + State *begin, *state; + + /* Build queue of child types */ + va_start (args, content); + types = type_queue (content, args); + va_end (args); + + /* chain types together */ + state = begin = state_new (format); + + for (list = types->head; list != NULL; list = list->next) + { + SType *child_type = list->data; + + state_add_transition (state, child_type->enter); + + state = state_new (format); + + transition_set_to_state (child_type->exit, state); + } + + g_queue_free (types); + + /* Return resulting fragment */ + if (forward) + { + type = (SType *)forward; + type->kind = TYPE_RECORD; + type->name = g_strdup (name); + } + else + { + type = type_new (format, TYPE_RECORD, name); + } + + type->enter = transition_new (format, BEGIN, type, NULL, begin); + type->exit = transition_new (format, END, type, state, NULL); + + return type; +} + +SType * +sformat_make_list (SFormat *format, + const char *name, + SForward *forward, + SType *child_type) +{ + SType *type; + State *list_state; + + if (forward) + { + type = (SType *)forward; + type->kind = TYPE_LIST; + type->name = g_strdup (name); + } + else + { + type = type_new (format, TYPE_LIST, name); + } + + list_state = state_new (format); + + type->enter = transition_new (format, BEGIN, type, NULL, list_state); + type->exit = transition_new (format, END, type, list_state, NULL); + + state_add_transition (list_state, child_type->enter); + transition_set_to_state (child_type->exit, list_state); + + return type; +} + +static SType * +type_new_value (SFormat *format, TypeKind kind, const char *name) +{ + SType *type = type_new (format, kind, name); + State *before, *after; + Transition *value; + + before = state_new (format); + after = state_new (format); + + type->enter = transition_new (format, BEGIN, type, NULL, before); + type->exit = transition_new (format, END, type, after, NULL); + value = transition_new (format, VALUE, type, before, after); + + return type; +} + +SType * +sformat_make_pointer (SFormat *format, + const char *name, + SForward *forward) +{ + SType *type = type_new_value (format, TYPE_POINTER, name); + type->target = (SType *)forward; + + return type; +} + +SType * +sformat_make_integer (SFormat *format, + const char *name) +{ + return type_new_value (format, TYPE_INTEGER, name); +} + +SType * +sformat_make_string (SFormat *format, + const char *name) +{ + return type_new_value (format, TYPE_STRING, name); +} + + +/* Consider adding unions at some point + * + * To be useful they should probably be anonymous, so that + * the union itself doesn't have a representation in the + * xml file. + * + * API: + * sformat_new_union (gpointer content1, ...); + * + * char *content = begin_get_union (); + * if (strcmp (content, ...) == 0) + * get_pointer (); + * else if (strcmp (content, ...) == 0) + * + * ; + * + * Annoying though, that we then won't have the nice one-to-one + * correspondence between begin()/end() calls and s + * Actually, we will probably have to have asdlfkj + * elements. That will make things a lot easier, and unions are + * still pretty useful if you put big things like lists in them. + * + * We may also consider adding anonymous records. These will + * not be able to have pointers associated with them though + * (because there wouldn't be a natural place + * + * + * Also consider adding the following data types: + * + * - Binary blobs of data, stored as base64 perhaps + * + * - floating point values. How do we store those portably + * without losing precision? Gnumeric may know. + * + * - enums, stored as strings + * + * - booleans. + */ + + +/* + * State + */ +static State * +state_new (SFormat *format) +{ + State *state = g_new0 (State, 1); + state->transitions = g_queue_new (); + + g_queue_push_tail (format->states, state); + + return state; +} + +static void +state_add_transition (State *state, + Transition *transition) +{ + g_queue_push_tail (state->transitions, transition); +} + +static void +state_free (State *state) +{ + g_queue_free (state->transitions); + g_free (state); +} + +/* + * Transition + */ +static Transition * +transition_new (SFormat *format, + TransitionKind kind, + SType *type, + State *from, + State *to) +{ + Transition *transition = g_new0 (Transition, 1); + + transition->type = type; + transition->kind = kind; + transition->to = to; + + if (from) + state_add_transition (from, transition); + + g_queue_push_tail (format->transitions, transition); + + return transition; +} + +static void +transition_free (Transition *transition) +{ + g_free (transition); +} + +static void +transition_set_to_state (Transition *transition, + State *to_state) +{ + transition->to = to_state; +} + +/* Context */ +struct SContext +{ + SFormat *format; + State *state; +}; + +SContext * +scontext_new (SFormat *format) +{ + SContext *context = g_new0 (SContext, 1); + + context->format = format; + context->state = format->begin; + + return context; +} + +static SType * +do_transition (SContext *context, + TransitionKind kind, + const char *element) +{ + GList *list; + + for (list = context->state->transitions->head; list; list = list->next) + { + Transition *transition = list->data; + + if (transition->kind == kind) + { + if (kind == VALUE || strcmp (transition->type->name, element) == 0) + { + context->state = transition->to; + return transition->type; + } + } + } + + return NULL; +} + +SType * +scontext_begin (SContext *context, + const char *element) +{ + return do_transition (context, BEGIN, element); +} + +SType * +scontext_text (SContext *context) +{ + return do_transition (context, VALUE, NULL); +} + +SType * +scontext_end (SContext *context, + const char *element) +{ + return do_transition (context, END, element); +} + +gboolean +scontext_is_finished (SContext *context) +{ + return context->state == context->format->end; +} + +void +scontext_free (SContext *context) +{ + g_free (context); +} + + + +/* assorted stuff */ +#if 0 +static const State * +state_transition_check (const State *state, + const char *element, + TransitionKind kind, + SType *type) +{ + GList *list; + + for (list = state->transitions->head; list; list = list->next) + { + Transition *transition = list->data; + + if (transition->kind == kind && + strcmp (element, transition->element) == 0) + { + *type = transition->type; + return transition->to; + } + } + + return NULL; +} + +static const State * +state_transition_begin (const State *state, const char *element, SType *type) +{ + return state_transition_check (state, element, BEGIN, type); +} + +static const State * +state_transition_end (const State *state, const char *element, SType *type) +{ + return state_transition_check (state, element, END, type); +} + +static const State * +state_transition_text (const State *state, SType *type, SType *target_type) +{ + GList *list; + + for (list = state->transitions->head; list; list = list->next) + { + Transition *transition = list->data; + + if (transition->kind == VALUE) + { + *type = transition->type; + + if (*type == TYPE_POINTER && target_type) + *target_type = transition->target_type; + + /* There will never be more than one allowed value transition for + * a given state + */ + return transition->to; + } + } + + return NULL; +} + +#endif diff --git a/sformat.h b/sformat.h new file mode 100644 index 00000000..484f7a61 --- /dev/null +++ b/sformat.h @@ -0,0 +1,67 @@ +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, 2006, Soeren Sandmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SFORMAT_H +#define SFORMAT_H + +typedef struct SFormat SFormat; +typedef struct SType SType; +typedef struct SForward SForward; +typedef struct SContext SContext; + +SFormat *sformat_new (void); +void sformat_free (SFormat *format); +void sformat_set_type (SFormat *format, + SType *type); +SForward *sformat_declare_forward (SFormat *format); +SType *sformat_make_record (SFormat *format, + const char *name, + SForward *forward, + SType *content, + ...); +SType *sformat_make_list (SFormat *format, + const char *name, + SForward *forward, + SType *type); +SType *sformat_make_pointer (SFormat *format, + const char *name, + SForward *type); +SType *sformat_make_integer (SFormat *format, + const char *name); +SType *sformat_make_string (SFormat *format, + const char *name); + +gboolean stype_is_record (SType *type); +gboolean stype_is_list (SType *type); +gboolean stype_is_pointer (SType *type); +gboolean stype_is_integer (SType *type); +gboolean stype_is_string (SType *type); +SType *stype_get_target_type (SType *type); +const char *stype_get_name (SType *type); + +SContext *scontext_new (SFormat *format); +SType *scontext_begin (SContext *context, + const char *element); +SType *scontext_text (SContext *context); +SType *scontext_end (SContext *context, + const char *element); +gboolean scontext_is_finished (SContext *context); +void scontext_free (SContext *context); + +#endif diff --git a/stackstash.c b/stackstash.c index d22c69fe..ba1a4cc3 100644 --- a/stackstash.c +++ b/stackstash.c @@ -281,6 +281,8 @@ stack_stash_new_from_root (StackNode *root) { StackStash *stash = create_stack_stash(); + stash->root = root; + build_hash_table (stash->root, stash); return stash;