mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
Primitive loading and saving.
Wed Mar 23 00:04:07 2005 Soeren Sandmann <sandmann@redhat.com> Primitive loading and saving. * sysprof.c (on_open_clicked): Hook up loading. * sfile.c: Add a copy of g_file_replace() from glib CVS HEAD. * sfile.c (add_string): Escape and quote the string * sfile.c (sfile_load): Initialize current_instruction and instructions_by_location * sfile.c (post_process_instructions_recurse): Handle NULL pointers properly. * sfile.c (handle_begin_element, handle_end_element, handle_text): Move error handling here from state_transition_begin/text/end. * sfile.c (handle_text): Discard whitespace-only strings * sfile.c (sfile_get_pointer, sfile_get_integer, sfile_get_string): expect both begin, value, and end transitions. * sfile.c (hook_up_pointers): Only treat instructions as pointer values when they are. Handle NULL targets properly. * sfile.c (get_number): Fix a few read-freed-data bugs * profile.c (profile_load): Call sfile_end_get() for the profile; build the nodes_by_objects hash table. Build the call tree. * profile.c (create_format): Don't store next pointer, but do store total, self and toplevel. * profile.c (make_hash_table): New function to build nodes_by_object hashtable from loaded data
This commit is contained in:
committed by
Søren Sandmann Pedersen
parent
64a1ac6355
commit
2af6447238
38
ChangeLog
38
ChangeLog
@ -1,3 +1,41 @@
|
||||
Wed Mar 23 00:04:07 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||
|
||||
Primitive loading and saving.
|
||||
|
||||
* sysprof.c (on_open_clicked): Hook up loading.
|
||||
|
||||
* sfile.c: Add a copy of g_file_replace() from glib CVS HEAD.
|
||||
|
||||
* sfile.c (add_string): Escape and quote the string
|
||||
|
||||
* sfile.c (sfile_load): Initialize current_instruction and
|
||||
instructions_by_location
|
||||
|
||||
* sfile.c (post_process_instructions_recurse): Handle NULL
|
||||
pointers properly.
|
||||
|
||||
* sfile.c (handle_begin_element, handle_end_element, handle_text):
|
||||
Move error handling here from state_transition_begin/text/end.
|
||||
|
||||
* sfile.c (handle_text): Discard whitespace-only strings
|
||||
|
||||
* sfile.c (sfile_get_pointer, sfile_get_integer,
|
||||
sfile_get_string): expect both begin, value, and end transitions.
|
||||
|
||||
* sfile.c (hook_up_pointers): Only treat instructions as pointer
|
||||
values when they are. Handle NULL targets properly.
|
||||
|
||||
* sfile.c (get_number): Fix a few read-freed-data bugs
|
||||
|
||||
* profile.c (profile_load): Call sfile_end_get() for the profile;
|
||||
build the nodes_by_objects hash table. Build the call tree.
|
||||
|
||||
* profile.c (create_format): Don't store next pointer, but do
|
||||
store total, self and toplevel.
|
||||
|
||||
* profile.c (make_hash_table): New function to build
|
||||
nodes_by_object hashtable from loaded data
|
||||
|
||||
Sat Mar 12 11:05:19 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||
|
||||
* sysprof-module.c: Fix small bug in add_timeout()
|
||||
|
||||
50
profile.c
50
profile.c
@ -22,7 +22,7 @@ update()
|
||||
static guint
|
||||
direct_hash_no_null (gconstpointer v)
|
||||
{
|
||||
g_assert (v);
|
||||
g_assert (v != NULL);
|
||||
return GPOINTER_TO_UINT (v);
|
||||
}
|
||||
|
||||
@ -78,7 +78,9 @@ create_format (void)
|
||||
sformat_new_pointer ("siblings", &node_type),
|
||||
sformat_new_pointer ("children", &node_type),
|
||||
sformat_new_pointer ("parent", &node_type),
|
||||
sformat_new_pointer ("next", &node_type),
|
||||
sformat_new_integer ("total"),
|
||||
sformat_new_integer ("self"),
|
||||
sformat_new_integer ("toplevel"),
|
||||
NULL)),
|
||||
NULL));
|
||||
}
|
||||
@ -109,7 +111,9 @@ serialize_call_tree (Node *node, SFileOutput *output)
|
||||
sfile_add_pointer (output, "siblings", node->siblings);
|
||||
sfile_add_pointer (output, "children", node->children);
|
||||
sfile_add_pointer (output, "parent", node->parent);
|
||||
sfile_add_pointer (output, "next", node->next);
|
||||
sfile_add_integer (output, "total", node->total);
|
||||
sfile_add_integer (output, "self", node->self);
|
||||
sfile_add_integer (output, "toplevel", node->toplevel);
|
||||
sfile_end_add (output, "node", node);
|
||||
|
||||
serialize_call_tree (node->siblings, output);
|
||||
@ -148,6 +152,25 @@ profile_save (Profile *profile,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
make_hash_table (Node *node, GHashTable *table)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
g_assert (node->object);
|
||||
|
||||
node->next = g_hash_table_lookup (table, node->object);
|
||||
g_hash_table_insert (table, node->object, node);
|
||||
|
||||
g_print ("added %s\n", node->object->name);
|
||||
|
||||
g_assert (node->siblings != 0x11);
|
||||
|
||||
make_hash_table (node->siblings, table);
|
||||
make_hash_table (node->children, table);
|
||||
}
|
||||
|
||||
Profile *
|
||||
profile_load (const char *filename, GError **err)
|
||||
{
|
||||
@ -164,6 +187,9 @@ profile_load (const char *filename, GError **err)
|
||||
|
||||
profile = g_new (Profile, 1);
|
||||
|
||||
profile->nodes_by_object =
|
||||
g_hash_table_new (direct_hash_no_null, g_direct_equal);
|
||||
|
||||
sfile_begin_get_record (input, "profile");
|
||||
|
||||
sfile_get_integer (input, "size", &profile->size);
|
||||
@ -178,11 +204,12 @@ profile_load (const char *filename, GError **err)
|
||||
sfile_get_string (input, "name", &obj->name);
|
||||
sfile_get_integer (input, "total", &obj->total);
|
||||
sfile_get_integer (input, "self", &obj->self);
|
||||
|
||||
|
||||
sfile_end_get (input, "object", obj);
|
||||
}
|
||||
sfile_end_get (input, "objects", NULL);
|
||||
|
||||
profile->call_tree = NULL;
|
||||
n = sfile_begin_get_list (input, "nodes");
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
@ -194,17 +221,28 @@ profile_load (const char *filename, GError **err)
|
||||
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
|
||||
sfile_get_pointer (input, "children", (gpointer *)&node->children);
|
||||
sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
|
||||
sfile_get_pointer (input, "next", (gpointer *)&node->next);
|
||||
sfile_get_integer (input, "total", &node->total);
|
||||
sfile_get_integer (input, "self", &node->self);
|
||||
sfile_get_integer (input, "toplevel", &node->toplevel);
|
||||
|
||||
sfile_end_get (input, "node", node);
|
||||
|
||||
if (!node->parent)
|
||||
if (!profile->call_tree)
|
||||
profile->call_tree = node;
|
||||
|
||||
g_assert (node->siblings != 0x11);
|
||||
}
|
||||
sfile_end_get (input, "nodes", NULL);
|
||||
sfile_end_get (input, "profile", NULL);
|
||||
|
||||
sformat_free (format);
|
||||
|
||||
/* FIXME: why don't we just store the root node? */
|
||||
while (profile->call_tree && profile->call_tree->parent)
|
||||
profile->call_tree = profile->call_tree->parent;
|
||||
|
||||
make_hash_table (profile->call_tree, profile->nodes_by_object);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
||||
576
sfile.c
576
sfile.c
@ -210,6 +210,7 @@ fragment_queue (Fragment *fragment1, va_list args)
|
||||
* 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,
|
||||
@ -400,11 +401,10 @@ static const State *
|
||||
state_transition_check (const State *state,
|
||||
const char *element,
|
||||
TransitionKind kind,
|
||||
SType *type,
|
||||
GError **err)
|
||||
SType *type)
|
||||
{
|
||||
GList *list;
|
||||
|
||||
|
||||
for (list = state->transitions->head; list; list = list->next)
|
||||
{
|
||||
Transition *transition = list->data;
|
||||
@ -417,27 +417,23 @@ state_transition_check (const State *state,
|
||||
}
|
||||
}
|
||||
|
||||
set_unknown_element_error (err, "<%s> or </%s> unexpected here", element, element);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const State *
|
||||
state_transition_begin (const State *state, const char *element,
|
||||
SType *type, GError **err)
|
||||
state_transition_begin (const State *state, const char *element, SType *type)
|
||||
{
|
||||
return state_transition_check (state, element, BEGIN, type, err);
|
||||
return state_transition_check (state, element, BEGIN, type);
|
||||
}
|
||||
|
||||
static const State *
|
||||
state_transition_end (const State *state, const char *element,
|
||||
SType *type, GError **err)
|
||||
state_transition_end (const State *state, const char *element, SType *type)
|
||||
{
|
||||
return state_transition_check (state, element, END, type, err);
|
||||
return state_transition_check (state, element, END, type);
|
||||
}
|
||||
|
||||
static const State *
|
||||
state_transition_text (const State *state, SType *type, GError **err)
|
||||
state_transition_text (const State *state, SType *type)
|
||||
{
|
||||
GList *list;
|
||||
|
||||
@ -454,9 +450,12 @@ state_transition_text (const State *state, SType *type, GError **err)
|
||||
*/
|
||||
return transition->to;
|
||||
}
|
||||
#if 0
|
||||
else
|
||||
g_print ("transition: %d (%s)\n", transition->kind, transition->element);
|
||||
#endif
|
||||
}
|
||||
|
||||
set_invalid_content_error (err, "Unexpected text data");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -518,18 +517,19 @@ get_number (const char *text, int *number)
|
||||
char *end;
|
||||
int result;
|
||||
char *stripped;
|
||||
|
||||
gboolean retval;
|
||||
|
||||
stripped = g_strstrip (g_strdup (text));
|
||||
result = strtol (stripped, &end, 10);
|
||||
|
||||
retval = (*end == '\0');
|
||||
|
||||
if (retval && number)
|
||||
*number = result;
|
||||
|
||||
g_free (stripped);
|
||||
|
||||
if (*end != '\0')
|
||||
return FALSE;
|
||||
|
||||
if (number)
|
||||
*number = result;
|
||||
|
||||
return TRUE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct SFileInput
|
||||
@ -582,10 +582,15 @@ sfile_get_pointer (SFileInput *file,
|
||||
const char *name,
|
||||
gpointer *location)
|
||||
{
|
||||
Instruction *instruction = file->current_instruction++;
|
||||
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);
|
||||
|
||||
instruction->u.pointer.location = location;
|
||||
|
||||
*location = (gpointer) 0xFedeAbe;
|
||||
@ -597,6 +602,11 @@ sfile_get_pointer (SFileInput *file,
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@ -604,12 +614,21 @@ sfile_get_integer (SFileInput *file,
|
||||
const char *name,
|
||||
int *integer)
|
||||
{
|
||||
Instruction *instruction = file->current_instruction++;
|
||||
Instruction *instruction;
|
||||
|
||||
instruction = file->current_instruction++;
|
||||
g_return_if_fail (instruction->type == TYPE_INTEGER &&
|
||||
strcmp (instruction->name, name) == 0);
|
||||
|
||||
instruction = file->current_instruction++;
|
||||
g_return_if_fail (instruction->type == TYPE_INTEGER);
|
||||
|
||||
if (integer)
|
||||
*integer = instruction->u.integer.value;
|
||||
|
||||
instruction = file->current_instruction++;
|
||||
g_return_if_fail (instruction->type == TYPE_INTEGER &&
|
||||
strcmp (instruction->name, name) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
@ -617,28 +636,52 @@ sfile_get_string (SFileInput *file,
|
||||
const char *name,
|
||||
char **string)
|
||||
{
|
||||
Instruction *instruction = file->current_instruction++;
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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->type == TYPE_POINTER)
|
||||
if (instruction->kind == VALUE &&
|
||||
instruction->type == TYPE_POINTER)
|
||||
{
|
||||
gpointer target_object =
|
||||
instruction->u.pointer.target_instruction->u.begin.end_instruction->u.end.object;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -705,16 +748,22 @@ 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, err);
|
||||
build->state = state_transition_begin (build->state, element_name, &instruction.type);
|
||||
if (!build->state)
|
||||
{
|
||||
set_unknown_element_error (err, "<%s> unexpected here", element_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: 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);
|
||||
}
|
||||
|
||||
@ -727,7 +776,12 @@ handle_end_element (GMarkupParseContext *context,
|
||||
BuildContext *build = user_data;
|
||||
Instruction instruction;
|
||||
|
||||
build->state = state_transition_end (build->state, element_name, &instruction.type, err);
|
||||
build->state = state_transition_end (build->state, element_name, &instruction.type);
|
||||
if (!build->state)
|
||||
{
|
||||
set_unknown_element_error (err, "</%s> unexpected here", element_name);
|
||||
return;
|
||||
}
|
||||
|
||||
instruction.name = g_strdup (element_name);
|
||||
instruction.kind = END;
|
||||
@ -739,7 +793,7 @@ static gboolean
|
||||
decode_text (const char *text, char **decoded)
|
||||
{
|
||||
int length = strlen (text);
|
||||
|
||||
|
||||
if (length < 2)
|
||||
return FALSE;
|
||||
|
||||
@ -761,11 +815,28 @@ handle_text (GMarkupParseContext *context,
|
||||
{
|
||||
BuildContext *build = user_data;
|
||||
Instruction instruction;
|
||||
|
||||
build->state = state_transition_text (build->state, &instruction.type, err);
|
||||
char *free_me;
|
||||
|
||||
text = free_me = g_strstrip (g_strdup (text));
|
||||
|
||||
if (strlen (text) == 0)
|
||||
goto out;
|
||||
|
||||
build->state = state_transition_text (build->state, &instruction.type);
|
||||
if (!build->state)
|
||||
{
|
||||
int line, ch;
|
||||
g_markup_parse_context_get_position (context, &line, &ch);
|
||||
#if 0
|
||||
g_print ("line: %d char: %d\n", line, ch);
|
||||
#endif
|
||||
set_invalid_content_error (err, "Unexpected text data");
|
||||
goto out;
|
||||
}
|
||||
|
||||
instruction.name = NULL;
|
||||
instruction.kind = VALUE;
|
||||
instruction.u.string.value = 0x01;
|
||||
|
||||
switch (instruction.type)
|
||||
{
|
||||
@ -773,47 +844,50 @@ handle_text (GMarkupParseContext *context,
|
||||
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;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case TYPE_INTEGER:
|
||||
if (!get_number (text, &instruction.u.integer.value))
|
||||
{
|
||||
set_invalid_content_error (err, "Contents '%s' of integer element not a number", text);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case TYPE_STRING:
|
||||
if (!decode_text (text, &instruction.u.string.value))
|
||||
{
|
||||
set_invalid_content_error (err, "Contents '%s' of text element is illformed", text);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
g_array_append_val (build->instructions, instruction);
|
||||
|
||||
out:
|
||||
g_free (free_me);
|
||||
}
|
||||
|
||||
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->type == TYPE_STRING)
|
||||
|
||||
if (instruction->kind == VALUE && instruction->type == TYPE_STRING)
|
||||
g_free (instruction->u.string.value);
|
||||
}
|
||||
|
||||
@ -928,19 +1002,31 @@ post_process_read_instructions (Instruction *instructions, int n_instructions, G
|
||||
{
|
||||
Instruction *instruction = &(instructions[i]);
|
||||
|
||||
if (instruction->type == TYPE_POINTER)
|
||||
if (instruction->kind == VALUE &&
|
||||
instruction->type == TYPE_POINTER)
|
||||
{
|
||||
int target_id = instruction->u.pointer.target_id;
|
||||
|
||||
Instruction *target = g_hash_table_lookup (instructions_by_id,
|
||||
GINT_TO_POINTER (target_id));
|
||||
|
||||
if (!target)
|
||||
if (target_id)
|
||||
{
|
||||
set_invalid_content_error (err, "Id %d doesn't reference any record or list\n",
|
||||
instruction->u.pointer.target_id);
|
||||
retval = FALSE;
|
||||
break;
|
||||
Instruction *target = g_hash_table_lookup (instructions_by_id,
|
||||
GINT_TO_POINTER (target_id));
|
||||
|
||||
if (target)
|
||||
{
|
||||
instruction->u.pointer.target_instruction = target;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_invalid_content_error (err, "Id %d doesn't reference any record or list\n",
|
||||
instruction->u.pointer.target_id);
|
||||
retval = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction->u.pointer.target_instruction = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1018,6 +1104,9 @@ 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);
|
||||
|
||||
return input;
|
||||
}
|
||||
@ -1050,8 +1139,7 @@ sfile_begin_add_record (SFileOutput *file,
|
||||
{
|
||||
Instruction instruction;
|
||||
|
||||
file->state = state_transition_begin (
|
||||
file->state, name, &instruction.type, NULL);
|
||||
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));
|
||||
@ -1068,8 +1156,7 @@ sfile_begin_add_list (SFileOutput *file,
|
||||
{
|
||||
Instruction instruction;
|
||||
|
||||
file->state = state_transition_begin (
|
||||
file->state, name, &instruction.type, NULL);
|
||||
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));
|
||||
@ -1093,8 +1180,7 @@ sfile_end_add (SFileOutput *file,
|
||||
return;
|
||||
}
|
||||
|
||||
file->state = state_transition_end (
|
||||
file->state, name, &instruction.type, NULL);
|
||||
file->state = state_transition_end (file->state, name, &instruction.type);
|
||||
|
||||
if (!file->state)
|
||||
{
|
||||
@ -1119,13 +1205,13 @@ sfile_check_value (SFileOutput *file,
|
||||
{
|
||||
SType tmp_type;
|
||||
|
||||
file->state = state_transition_begin (file->state, name, &tmp_type, NULL);
|
||||
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);
|
||||
file->state = state_transition_text (file->state, &type);
|
||||
g_return_if_fail (file->state && tmp_type == type);
|
||||
|
||||
file->state = state_transition_end (file->state, name, &type, NULL);
|
||||
file->state = state_transition_end (file->state, name, &type);
|
||||
g_return_if_fail (file->state && tmp_type == type);
|
||||
}
|
||||
|
||||
@ -1238,7 +1324,7 @@ post_process_write_instructions (SFileOutput *sfile)
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1269,7 +1355,27 @@ add_integer (GString *output, int value)
|
||||
static void
|
||||
add_string (GString *output, const char *str)
|
||||
{
|
||||
#if 0
|
||||
/* FIXME: strings need to be encoded so that special
|
||||
* xml characters can be added, and so they don't get
|
||||
* confused with the deletion of whitespace-only
|
||||
* text entries
|
||||
*/
|
||||
g_string_append_printf (output, "%s", str);
|
||||
#endif
|
||||
|
||||
int i;
|
||||
g_string_append_c (output, '\"');
|
||||
for (i = 0; str[i] != '\0'; ++i)
|
||||
{
|
||||
if (str[i] == '\"')
|
||||
g_string_append_printf (output, "%s", "\\\"");
|
||||
else if (str[i] == '\\')
|
||||
g_string_append_printf (output, "\\\\");
|
||||
else
|
||||
g_string_append_c (output, str[i]);
|
||||
}
|
||||
g_string_append_c (output, '\"');
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1296,6 +1402,12 @@ add_nl (GString *output)
|
||||
g_string_append_c (output, '\n');
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_replace (const gchar *filename,
|
||||
const gchar *contents,
|
||||
gssize length,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
sfile_output_save (SFileOutput *sfile,
|
||||
const char *filename,
|
||||
@ -1359,13 +1471,13 @@ sfile_output_save (SFileOutput *sfile,
|
||||
/* FIXME: don't dump this to stdout */
|
||||
g_print (output->str);
|
||||
|
||||
#if 0
|
||||
/* FIXME, cut-and-paste the g_file_write() implementation
|
||||
* as long as it isn't in glib
|
||||
*/
|
||||
retval = g_file_write (filename, output->str, - 1, err);
|
||||
#endif
|
||||
retval = file_replace (filename, output->str, - 1, err);
|
||||
#if 0
|
||||
retval = TRUE;
|
||||
#endif
|
||||
|
||||
g_string_free (output, TRUE);
|
||||
|
||||
@ -1378,3 +1490,333 @@ sfile_output_free (SFileOutput *sfile)
|
||||
{
|
||||
/* FIXME */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* A copy of g_file_replace() because I don't want to depend on
|
||||
* GLib HEAD
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static gboolean
|
||||
rename_file (const char *old_name,
|
||||
const char *new_name,
|
||||
GError **err)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_umask_permissions (int fd,
|
||||
GError **err)
|
||||
{
|
||||
#ifdef G_OS_WIN32
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
/* 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
|
||||
{
|
||||
/* parent */
|
||||
int status;
|
||||
|
||||
waitpid (pid, &status, 0);
|
||||
|
||||
if (WIFEXITED (status))
|
||||
{
|
||||
int chmod_errno = WEXITSTATUS (status);
|
||||
|
||||
if (chmod_errno == 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static gchar *
|
||||
write_to_temp_file (const gchar *contents,
|
||||
gssize length,
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
close (fd);
|
||||
g_unlink (tmp_name);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
retval = g_strdup (tmp_name);
|
||||
|
||||
out:
|
||||
g_free (tmp_name);
|
||||
g_free (display_name);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
file_replace (const gchar *filename,
|
||||
const gchar *contents,
|
||||
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)
|
||||
{
|
||||
retval = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#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))
|
||||
{
|
||||
g_unlink (tmp_filename);
|
||||
g_propagate_error (error, rename_error);
|
||||
retval = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!rename_file (tmp_filename, filename, error))
|
||||
{
|
||||
g_unlink (tmp_filename);
|
||||
retval = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
retval = TRUE;
|
||||
|
||||
out:
|
||||
g_free (tmp_filename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
42
sysprof.c
42
sysprof.c
@ -56,6 +56,22 @@ struct Application
|
||||
|
||||
int timeout_id;
|
||||
int generating_profile;
|
||||
|
||||
gboolean profile_from_file; /* FIXME: This is a kludge. Figure out how
|
||||
* to maintain the application model properly
|
||||
*
|
||||
* The fundamental issue is that the state of
|
||||
* widgets is controlled by two different
|
||||
* entities:
|
||||
*
|
||||
* The user clicks on them, changing their
|
||||
* state.
|
||||
*
|
||||
* The application model changes, changing their
|
||||
* state.
|
||||
*
|
||||
* Model/View/Controller is a possibility.
|
||||
*/
|
||||
};
|
||||
|
||||
static void
|
||||
@ -290,6 +306,7 @@ delete_data (Application *app)
|
||||
process_flush_caches ();
|
||||
app->n_samples = 0;
|
||||
queue_show_samples (app);
|
||||
app->profile_from_file = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -349,6 +366,7 @@ fill_main_list (Application *app)
|
||||
G_TYPE_POINTER);
|
||||
|
||||
objects = profile_get_objects (profile);
|
||||
g_print ("got %d objects\n", g_list_length (objects));
|
||||
for (list = objects; list != NULL; list = list->next)
|
||||
{
|
||||
ProfileObject *object = list->data;
|
||||
@ -414,12 +432,12 @@ on_profile_toggled (gpointer widget, gpointer data)
|
||||
|
||||
if (gtk_toggle_tool_button_get_active (widget))
|
||||
{
|
||||
if (app->profile)
|
||||
if (app->profile && !app->profile_from_file)
|
||||
{
|
||||
profile_free (app->profile);
|
||||
app->profile = NULL;
|
||||
}
|
||||
|
||||
|
||||
ensure_profile (app);
|
||||
}
|
||||
}
|
||||
@ -486,7 +504,27 @@ on_save_as_clicked (gpointer widget, gpointer data)
|
||||
static void
|
||||
on_open_clicked (gpointer widget, gpointer data)
|
||||
{
|
||||
#if 0
|
||||
sorry (NULL, "Open is not implemented yet. (Fortunately, neither is saving),");
|
||||
#endif
|
||||
Application *app = data;
|
||||
GError *err = NULL;
|
||||
Profile *profile = profile_load ("name.profile", &err);
|
||||
if (!profile)
|
||||
sorry (NULL, "Could not open: %s\n", err->message);
|
||||
else
|
||||
{
|
||||
delete_data (app);
|
||||
|
||||
app->state = DISPLAYING;
|
||||
|
||||
app->profile = profile;
|
||||
app->profile_from_file = TRUE;
|
||||
|
||||
fill_main_list (app);
|
||||
|
||||
update_sensitivity (app);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
Reference in New Issue
Block a user