mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-09 22:50:54 +00:00
Merge stackstash-reorg branch into HEAD
2005-10-30 Soren Sandmann <sandmann@redhat.com> * Merge stackstash-reorg branch into HEAD
This commit is contained in:
committed by
Søren Sandmann Pedersen
parent
3783be00a8
commit
dff4affaab
187
ChangeLog
187
ChangeLog
@ -1,3 +1,79 @@
|
|||||||
|
2005-10-30 Soren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* Merge stackstash-reorg branch into HEAD
|
||||||
|
|
||||||
|
2005-10-30 Soren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* Makefile.am, sysprof.c, sysprof-text.c, collector.[ch]: Rename
|
||||||
|
profiler -> collector
|
||||||
|
|
||||||
|
2005-10-30 Soren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c (profile_load): Reenable loading.
|
||||||
|
|
||||||
|
2005-10-30 Soren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c (profile_save): Reenable saving.
|
||||||
|
|
||||||
|
Sat Oct 29 21:37:42 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* stackstash.c (stack_stash_foreach): Rename
|
||||||
|
stack_stash_foreach_reversed() to stack_stash_foreach(). Delete
|
||||||
|
the old, unused stack_stash_foreach().
|
||||||
|
|
||||||
|
* stackstash.h: Remove stack_stash_foreach_reversed().
|
||||||
|
|
||||||
|
* profiler.c: Use new name.
|
||||||
|
|
||||||
|
Sat Oct 29 18:37:37 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* TODO: Updates
|
||||||
|
|
||||||
|
Sat Oct 29 17:38:01 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* stackstash.[ch]: Add stack_stash_get_root().
|
||||||
|
|
||||||
|
* profile.c (profile_get_size): Make this function work again.
|
||||||
|
|
||||||
|
Sat Oct 29 16:58:22 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c: Delete all the obsolete stuff related to call tree.
|
||||||
|
|
||||||
|
* TODO: update
|
||||||
|
|
||||||
|
Sat Oct 29 16:52:32 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* TODO: Updates
|
||||||
|
|
||||||
|
* profile.c: Comment out a lot of unused stuff. Also temporarily
|
||||||
|
comment out loading and saving.
|
||||||
|
|
||||||
|
Sat Oct 29 16:45:34 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c (compute_total): New function.
|
||||||
|
|
||||||
|
* profile.c (profile_list_callers): Port this function over to use
|
||||||
|
StackNodes instead.
|
||||||
|
|
||||||
|
Sat Oct 29 16:22:28 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c (build_object_list): Make this function allocate
|
||||||
|
the ProfileObjects.
|
||||||
|
|
||||||
|
* stackstash.[ch]: Add stack_stash_foreach_by_address()
|
||||||
|
|
||||||
|
* profile.c (profile_get_objects): Use it here to reimplement
|
||||||
|
profile_get_objects() in terms of the stackstash.
|
||||||
|
|
||||||
|
Sat Oct 29 15:33:07 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c (profile_new): Add stash field to profile.
|
||||||
|
|
||||||
|
* stackstash.[ch]: Add stack_node_list_leaves();
|
||||||
|
|
||||||
|
* profile.c (profile_create_descendants): Port this function over
|
||||||
|
to use StackNodes instead.
|
||||||
|
|
||||||
Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com>
|
Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
Fix crash pointed reported by Rudi Chiarito.
|
Fix crash pointed reported by Rudi Chiarito.
|
||||||
@ -7,6 +83,21 @@ Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com>
|
|||||||
|
|
||||||
* sysprof.c (on_read): Only trace if n_addresses != 0.
|
* sysprof.c (on_read): Only trace if n_addresses != 0.
|
||||||
|
|
||||||
|
Sat Oct 29 03:47:03 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.[ch], sysprof.c: Get rid of ProfileObject for callers
|
||||||
|
and descendants.
|
||||||
|
|
||||||
|
* TODO: updates.
|
||||||
|
|
||||||
|
Sat Oct 29 02:57:34 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* stackstash.[ch]: Export the StackNode struct, add new
|
||||||
|
nodes_by_data hashtable to StackStash object, keep track of
|
||||||
|
whether objects are toplevels.
|
||||||
|
|
||||||
|
* TODO: some updates.
|
||||||
|
|
||||||
Sat Oct 29 14:29:55 2005 Søren Sandmann <sandmann@redhat.com>
|
Sat Oct 29 14:29:55 2005 Søren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
* README, TODO: updates
|
* README, TODO: updates
|
||||||
@ -24,6 +115,102 @@ Tue Oct 11 22:40:24 2005 Soeren Sandmann <sandmann@redhat.com>
|
|||||||
constants, but this time make sure we won't divide by 0 or
|
constants, but this time make sure we won't divide by 0 or
|
||||||
anything like that.
|
anything like that.
|
||||||
|
|
||||||
|
Mon Oct 10 22:50:57 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* Merge in changes from HEAD
|
||||||
|
|
||||||
|
Thu Oct 6 22:28:39 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* stackstash.c (do_callback): Call stack func if node->size > 0,
|
||||||
|
not if node->children != NULL
|
||||||
|
* stackstash.c (do_reversed_callback): same
|
||||||
|
* profile.c (generate_presentation_name): Delete this function
|
||||||
|
* profile.c (generate_key): Delete this function.
|
||||||
|
|
||||||
|
Wed Oct 5 23:57:08 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* TODO: Updates
|
||||||
|
|
||||||
|
* profile.c (generate_call_tree): Delete all the process stuff
|
||||||
|
|
||||||
|
* profile.c (node_add_trace): Delete process argument
|
||||||
|
|
||||||
|
* profile.c (lookup_profile_object): Don't generate a string key,
|
||||||
|
just use the address directly.
|
||||||
|
|
||||||
|
* profile.c (ensure_profile_object): Use the address as
|
||||||
|
presentation name.
|
||||||
|
|
||||||
|
* profiler.c (profiler_create_profile): Pass in the resolved
|
||||||
|
stash.
|
||||||
|
|
||||||
|
Sun Oct 2 21:08:16 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* sysprof.c (on_delete): Work around glib bug 317775
|
||||||
|
|
||||||
|
* sysprof-text.c (signal_handler): Work around glib bug 317775
|
||||||
|
|
||||||
|
Sun Oct 2 16:31:37 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* stackstash.c (stack_stash_foreach_reversed): Add this function
|
||||||
|
|
||||||
|
* process.c: Add a per-process undefined symbol.
|
||||||
|
|
||||||
|
* profiler.c (resolve_symbols): Add code to do symbol resolution
|
||||||
|
here.
|
||||||
|
|
||||||
|
* TODO: Updates
|
||||||
|
|
||||||
|
Sat Oct 1 18:32:52 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.h, sysprof.c: Remove some unnecessary includes.
|
||||||
|
|
||||||
|
Sat Oct 1 18:12:44 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profiler.[ch]: Add profiler_get_n_samples(); add unused
|
||||||
|
empty_file_descriptor()
|
||||||
|
|
||||||
|
* profile.h: Add include guards
|
||||||
|
|
||||||
|
* process.[ch]: Delete process_flush_caches();
|
||||||
|
|
||||||
|
* helper.[ch]: Remove these files
|
||||||
|
|
||||||
|
* sysprof.c: Use the new profiler class.
|
||||||
|
|
||||||
|
* sysprof-text.c: Use the new profiler class
|
||||||
|
|
||||||
|
* Makefile.am: Various cleanups.
|
||||||
|
|
||||||
|
Sat Oct 1 17:05:43 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profiler.[ch]: New files containing a profiler class with code
|
||||||
|
that can be shared between gui and command line.
|
||||||
|
|
||||||
|
* sysprof.c (main): Remove some commented out code
|
||||||
|
|
||||||
|
* stackstash.c (do_callback): Store the trace on the stack instead
|
||||||
|
of allocating it dynamically.
|
||||||
|
|
||||||
|
Sat Oct 1 01:50:15 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* stackstash.c (sstack_stash_add_trace): Simplify this function a
|
||||||
|
lot.
|
||||||
|
|
||||||
|
* helper.c: Include "process.h"
|
||||||
|
|
||||||
|
Sat Oct 1 01:29:06 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
|
* profile.c (generate_object_table, generate_call_tree): Don't
|
||||||
|
take the process as an argument. Instead dig it out of the
|
||||||
|
stacktrace.
|
||||||
|
|
||||||
|
* helper.c (add_trace_to_stash): New file, that adds a stacktrace
|
||||||
|
and a process to a stackstash.
|
||||||
|
|
||||||
|
* stackstash.[ch]: Remove all traces of the concept of
|
||||||
|
Process. Simplify stack_node_add_trace() somewhat.
|
||||||
|
|
||||||
Mon Oct 10 22:49:03 2005 Soeren Sandmann <sandmann@redhat.com>
|
Mon Oct 10 22:49:03 2005 Soeren Sandmann <sandmann@redhat.com>
|
||||||
|
|
||||||
* module/sysprof-module.c: Delete lots of commented-out code.
|
* module/sysprof-module.c: Delete lots of commented-out code.
|
||||||
|
|||||||
85
Makefile.am
85
Makefile.am
@ -2,67 +2,54 @@ SUBDIRS = $(MODULE_SUBDIR)
|
|||||||
DIST_SUBDIRS = module
|
DIST_SUBDIRS = module
|
||||||
|
|
||||||
bin_PROGRAMS = sysprof sysprof-text
|
bin_PROGRAMS = sysprof sysprof-text
|
||||||
pkgdata_DATA = sysprof.glade sysprof-icon.png
|
|
||||||
|
|
||||||
sysprof_SOURCES = \
|
SYSPROF_CORE = \
|
||||||
binfile.h \
|
binfile.h \
|
||||||
binfile.c \
|
binfile.c \
|
||||||
process.h \
|
collector.c \
|
||||||
process.c \
|
collector.h \
|
||||||
profile.h \
|
process.h \
|
||||||
profile.c \
|
process.c \
|
||||||
sfile.h \
|
profile.h \
|
||||||
sfile.c \
|
profile.c \
|
||||||
stackstash.h \
|
sfile.h \
|
||||||
stackstash.c \
|
sfile.c \
|
||||||
module/sysprof-module.h \
|
stackstash.h \
|
||||||
sysprof.c \
|
stackstash.c \
|
||||||
treeviewutils.h \
|
module/sysprof-module.h \
|
||||||
treeviewutils.c \
|
watch.h \
|
||||||
watch.h \
|
|
||||||
watch.c
|
watch.c
|
||||||
|
|
||||||
sysprof_text_SOURCES = \
|
sysprof_SOURCES = \
|
||||||
binfile.h \
|
$(SYSPROF_CORE) \
|
||||||
binfile.c \
|
treeviewutils.h \
|
||||||
process.h \
|
treeviewutils.c \
|
||||||
process.c \
|
sysprof.c
|
||||||
profile.h \
|
|
||||||
profile.c \
|
sysprof_text_SOURCES = \
|
||||||
sfile.h \
|
$(SYSPROF_CORE) \
|
||||||
sfile.c \
|
signal-handler.h \
|
||||||
stackstash.h \
|
signal-handler.c \
|
||||||
stackstash.c \
|
sysprof-text.c
|
||||||
module/sysprof-module.h \
|
|
||||||
signal-handler.h \
|
|
||||||
signal-handler.c \
|
|
||||||
sysprof-text.c \
|
|
||||||
treeviewutils.h \
|
|
||||||
treeviewutils.c \
|
|
||||||
watch.h \
|
|
||||||
watch.c
|
|
||||||
|
|
||||||
sysprof_LDADD = $(DEP_LIBS)
|
sysprof_LDADD = $(DEP_LIBS)
|
||||||
|
|
||||||
sysprof_text_LDADD = $(DEP_LIBS)
|
sysprof_text_LDADD = $(DEP_LIBS)
|
||||||
|
|
||||||
INCLUDES = \
|
pixmapsdir = $(datadir)/pixmaps
|
||||||
$(DEP_CFLAGS) \
|
|
||||||
-DDATADIR=\"$(pkgdatadir)\" \
|
|
||||||
-DPIXMAPDIR=\"$(datadir)/pixmaps\"
|
|
||||||
|
|
||||||
# memprof.desktop
|
INCLUDES = \
|
||||||
# memprof.spec.in
|
$(DEP_CFLAGS) \
|
||||||
|
-DDATADIR=\"$(pkgdatadir)\" \
|
||||||
|
-DPIXMAPDIR=\"$(pixmapsdir)\"
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
sysprof.glade \
|
module/sysprof-module.c \
|
||||||
sysprof-icon.png \
|
module/sysprof-module.h \
|
||||||
module/sysprof-module.c \
|
|
||||||
module/sysprof-module.h \
|
|
||||||
module/Makefile
|
module/Makefile
|
||||||
|
|
||||||
pixmapsdir = $(datadir)/pixmaps
|
dist_pkgdata_DATA = sysprof.glade
|
||||||
pixmaps_DATA = sysprof-icon.png
|
dist_pixmaps_DATA = sysprof-icon.png
|
||||||
|
|
||||||
insert-module:
|
insert-module:
|
||||||
modprobe -r sysprof-module
|
modprobe -r sysprof-module
|
||||||
|
|||||||
3
NEWS
3
NEWS
@ -0,0 +1,3 @@
|
|||||||
|
- New 'everything' object
|
||||||
|
- New commandline version
|
||||||
|
- Assign time spent in kernel to the user process responsible
|
||||||
|
|||||||
140
TODO
140
TODO
@ -11,9 +11,9 @@ Before 1.0.1:
|
|||||||
|
|
||||||
See also http://www.fedoraproject.org/wiki/Extras/KernelModuleProposal
|
See also http://www.fedoraproject.org/wiki/Extras/KernelModuleProposal
|
||||||
|
|
||||||
Before 1.2:
|
Someone already did create a package - should be googlable.
|
||||||
|
|
||||||
* Crash reported by Rudi Chiarito with n_addrs == 0.
|
Before 1.2:
|
||||||
|
|
||||||
* Find out why we get hangs with rawhide kernels. This only happens with the
|
* Find out why we get hangs with rawhide kernels. This only happens with the
|
||||||
'trace "current"' code. See this mail:
|
'trace "current"' code. See this mail:
|
||||||
@ -25,6 +25,54 @@ Before 1.2:
|
|||||||
|
|
||||||
(Reported by Kjartan Marass).
|
(Reported by Kjartan Marass).
|
||||||
|
|
||||||
|
- Fix bugs/performance issues:
|
||||||
|
- total should probably be cached so that compute_total() doesn't
|
||||||
|
take 80% of the time to generate a profile.
|
||||||
|
- decorate_node should be done lazily
|
||||||
|
- Find out why we sometimes get completely ridicoulous stacktraces,
|
||||||
|
where main seems to be called from within Xlib etc. This happens
|
||||||
|
even after restarting everything.
|
||||||
|
- It looks like the stackstash-reorg code confuses "main" from
|
||||||
|
unrelated processes. - currently it looks like if multiple
|
||||||
|
"main"s are present, only one gets listed in the object list.
|
||||||
|
- Numbers in caller view are completely screwed up.
|
||||||
|
- It looks like it sometimes gets confused with similar but different
|
||||||
|
processess: Something like:
|
||||||
|
process a spends 80% in foo() called from bar()
|
||||||
|
process b spends 1% in foo() called from baz()
|
||||||
|
we get reports of baz() using > 80% of the time.
|
||||||
|
Or something.
|
||||||
|
|
||||||
|
- make the things we put in a stackstash real
|
||||||
|
objects so that
|
||||||
|
- we can save them
|
||||||
|
- they will know how to delete the presentation
|
||||||
|
names and themselves (through a virtual function)
|
||||||
|
- they can contain markup etc.
|
||||||
|
- a more pragmatic approach might be to just walk the tree and
|
||||||
|
save it.
|
||||||
|
|
||||||
|
- make stasckstash ref counted
|
||||||
|
|
||||||
|
- plug all the leaks
|
||||||
|
- don't leak the presentation strings/objects
|
||||||
|
- loading and saving probably leak right now
|
||||||
|
|
||||||
|
- think about loading and saving. Goals
|
||||||
|
- Can load 1.0 profiles
|
||||||
|
- Don't export too much of stackstashes to the rest of the
|
||||||
|
app
|
||||||
|
|
||||||
|
- Reorganise stackstash and profile
|
||||||
|
|
||||||
|
- Remaining TODO before merging into head:
|
||||||
|
|
||||||
|
- rename profiler->collector
|
||||||
|
|
||||||
|
* Consider renaming profiler.[ch] to collector.[ch]
|
||||||
|
|
||||||
|
* Make sure sysprof-text is not linked to gtk+
|
||||||
|
|
||||||
* Figure out how to make sfile.[ch] use less memory.
|
* Figure out how to make sfile.[ch] use less memory.
|
||||||
- In general clean sfile.[ch] up a little:
|
- In general clean sfile.[ch] up a little:
|
||||||
- split out dfa in its own generic class
|
- split out dfa in its own generic class
|
||||||
@ -192,39 +240,6 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html
|
|||||||
so (can we know the size in advance?))
|
so (can we know the size in advance?))
|
||||||
- instead of what we do now: set the busy cursor unconditionally
|
- instead of what we do now: set the busy cursor unconditionally
|
||||||
|
|
||||||
- Reorganise stackstash and profile
|
|
||||||
|
|
||||||
- stackstash should just take traces of addresses without knowing
|
|
||||||
anything about what those addresses mean.
|
|
||||||
|
|
||||||
- stacktraces should then begin with a process
|
|
||||||
|
|
||||||
- stackstash should be extended so that the "create_descendant"
|
|
||||||
and "create_ancestor" code in profile.c can use it directly.
|
|
||||||
At that point, get rid of the profile tree, and rename
|
|
||||||
profile.c to analyze.c.
|
|
||||||
|
|
||||||
- the profile tree will then just be a stackstash where the
|
|
||||||
addresses are presentation strings instead.
|
|
||||||
|
|
||||||
- Doing a profile will then amount to converting the raw stash
|
|
||||||
to one where the addresses have been looked up and converted to
|
|
||||||
presentation strings.
|
|
||||||
|
|
||||||
-=-=
|
|
||||||
|
|
||||||
- profile should take traces of pointers to presentation
|
|
||||||
objects without knowing anything about these presentation
|
|
||||||
objects.
|
|
||||||
|
|
||||||
- For each stack node, compute a presentation object
|
|
||||||
(probably need to export opaque stacknode objects
|
|
||||||
with set/get_user_data)
|
|
||||||
|
|
||||||
- Send each stack trace to the profile module, along with
|
|
||||||
presentation objects. Maybe just a map from stack nodes
|
|
||||||
to presentation objects.
|
|
||||||
|
|
||||||
- Charge 'self' properly to processes that don't get any stack trace at all
|
- Charge 'self' properly to processes that don't get any stack trace at all
|
||||||
(probably we get that for free with stackstash reorganisation)
|
(probably we get that for free with stackstash reorganisation)
|
||||||
|
|
||||||
@ -249,7 +264,8 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html
|
|||||||
- Add support for line numbers within functions
|
- Add support for line numbers within functions
|
||||||
- Possibly a special "view details" mode, assuming that
|
- Possibly a special "view details" mode, assuming that
|
||||||
the details of a function are not that interesting
|
the details of a function are not that interesting
|
||||||
together with a tree.
|
together with a tree. (Could add radio buttons somewhere in
|
||||||
|
in the right pane).
|
||||||
- rethink caller list, not terribly useful at the moment. Federico suggested
|
- rethink caller list, not terribly useful at the moment. Federico suggested
|
||||||
listing all ancestors.
|
listing all ancestors.
|
||||||
|
|
||||||
@ -452,12 +468,64 @@ Later:
|
|||||||
|
|
||||||
DONE:
|
DONE:
|
||||||
|
|
||||||
|
* Crash reported by Rudi Chiarito with n_addrs == 0.
|
||||||
|
|
||||||
* Find out what distributions it actually works on
|
* Find out what distributions it actually works on
|
||||||
(ask for sucess/failure-stories in 1.0 releases)
|
(ask for sucess/failure-stories in 1.0 releases)
|
||||||
|
|
||||||
* Add note in README about Ubuntu and Debian -dbg packages and how to get
|
* Add note in README about Ubuntu and Debian -dbg packages and how to get
|
||||||
debug symbols for X there.
|
debug symbols for X there.
|
||||||
|
|
||||||
|
stackstash reorg:
|
||||||
|
|
||||||
|
- make loading and saving work again.
|
||||||
|
- make stashes loadable and savable.
|
||||||
|
- add a way to convert 1.0 files to stashes
|
||||||
|
|
||||||
|
- Get rid of remaining uses of stack_stash_foreach(), then
|
||||||
|
rename stack_stash_foreach_reversed() to
|
||||||
|
stack_stash_foreach()
|
||||||
|
|
||||||
|
- stackstash should just take traces of addresses without knowing
|
||||||
|
anything about what those addresses mean.
|
||||||
|
|
||||||
|
- stacktraces should then begin with a process
|
||||||
|
|
||||||
|
- stackstash should be extended so that the "create_descendant"
|
||||||
|
and "create_ancestor" code in profile.c can use it directly.
|
||||||
|
At that point, get rid of the profile tree, and rename
|
||||||
|
profile.c to analyze.c.
|
||||||
|
|
||||||
|
- the profile tree will then just be a stackstash where the
|
||||||
|
addresses are presentation strings instead.
|
||||||
|
|
||||||
|
- Doing a profile will then amount to converting the raw stash
|
||||||
|
to one where the addresses have been looked up and converted to
|
||||||
|
presentation strings.
|
||||||
|
|
||||||
|
-=-=
|
||||||
|
|
||||||
|
- profile should take traces of pointers to presentation
|
||||||
|
objects without knowing anything about these presentation
|
||||||
|
objects.
|
||||||
|
|
||||||
|
- For each stack node, compute a presentation object
|
||||||
|
(probably need to export opaque stacknode objects
|
||||||
|
with set/get_user_data)
|
||||||
|
|
||||||
|
- Send each stack trace to the profile module, along with
|
||||||
|
presentation objects. Maybe just a map from stack nodes
|
||||||
|
to presentation objects.
|
||||||
|
|
||||||
|
- Make the Profile class use the stash directly instead of
|
||||||
|
building its own copy.
|
||||||
|
- store a stash in the profile class
|
||||||
|
- make sure descendants and callers can be
|
||||||
|
built from it.
|
||||||
|
- get rid of other stuff in the profile
|
||||||
|
struct
|
||||||
|
|
||||||
|
|
||||||
* Before 1.0:
|
* Before 1.0:
|
||||||
|
|
||||||
- Update version numbers in source
|
- Update version numbers in source
|
||||||
|
|||||||
300
collector.c
Normal file
300
collector.c
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
#include "stackstash.h"
|
||||||
|
#include "collector.h"
|
||||||
|
#include "module/sysprof-module.h"
|
||||||
|
#include "watch.h"
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct Collector
|
||||||
|
{
|
||||||
|
CollectorFunc callback;
|
||||||
|
gpointer data;
|
||||||
|
|
||||||
|
StackStash * stash;
|
||||||
|
int fd;
|
||||||
|
GTimeVal latest_reset;
|
||||||
|
int n_samples;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* callback is called whenever a new sample arrives */
|
||||||
|
Collector *
|
||||||
|
collector_new (CollectorFunc callback,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
Collector *collector = g_new0 (Collector, 1);
|
||||||
|
|
||||||
|
collector->callback = callback;
|
||||||
|
collector->data = data;
|
||||||
|
collector->fd = -1;
|
||||||
|
collector->stash = NULL;
|
||||||
|
|
||||||
|
collector_reset (collector);
|
||||||
|
|
||||||
|
return collector;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
timeval_to_ms (const GTimeVal *timeval)
|
||||||
|
{
|
||||||
|
return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
time_diff (const GTimeVal *first,
|
||||||
|
const GTimeVal *second)
|
||||||
|
{
|
||||||
|
double first_ms = timeval_to_ms (first);
|
||||||
|
double second_ms = timeval_to_ms (second);
|
||||||
|
|
||||||
|
return first_ms - second_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RESET_DEAD_PERIOD 250
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_trace_to_stash (SysprofStackTrace *trace,
|
||||||
|
StackStash *stash)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
gulong *addrs;
|
||||||
|
Process *process = process_get_from_pid (trace->pid);
|
||||||
|
|
||||||
|
addrs = g_new (gulong, trace->n_addresses + 1);
|
||||||
|
|
||||||
|
for (i = 0; i < trace->n_addresses; ++i)
|
||||||
|
{
|
||||||
|
process_ensure_map (process, trace->pid,
|
||||||
|
(gulong)trace->addresses[i]);
|
||||||
|
|
||||||
|
addrs[i] = (gulong)trace->addresses[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs[i] = (gulong)process;
|
||||||
|
|
||||||
|
stack_stash_add_trace (
|
||||||
|
stash, addrs, trace->n_addresses + 1, 1);
|
||||||
|
|
||||||
|
g_free (addrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_read (gpointer data)
|
||||||
|
{
|
||||||
|
SysprofStackTrace trace;
|
||||||
|
Collector *collector = data;
|
||||||
|
GTimeVal now;
|
||||||
|
int rd;
|
||||||
|
|
||||||
|
rd = read (collector->fd, &trace, sizeof (trace));
|
||||||
|
|
||||||
|
if (rd == -1 && errno == EWOULDBLOCK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_get_current_time (&now);
|
||||||
|
|
||||||
|
/* After a reset we ignore samples for a short period so that
|
||||||
|
* a reset will actually cause 'samples' to become 0
|
||||||
|
*/
|
||||||
|
if (time_diff (&now, &collector->latest_reset) < RESET_DEAD_PERIOD)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int i;
|
||||||
|
g_print ("pid: %d\n", trace.pid);
|
||||||
|
for (i=0; i < trace.n_addresses; ++i)
|
||||||
|
g_print ("rd: %08x\n", trace.addresses[i]);
|
||||||
|
g_print ("-=-\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rd > 0)
|
||||||
|
{
|
||||||
|
add_trace_to_stash (&trace, collector->stash);
|
||||||
|
|
||||||
|
collector->n_samples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collector->callback)
|
||||||
|
collector->callback (collector->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
load_module (void)
|
||||||
|
{
|
||||||
|
int exit_status = -1;
|
||||||
|
char *dummy1, *dummy2;
|
||||||
|
|
||||||
|
if (g_spawn_command_line_sync ("/sbin/modprobe sysprof-module",
|
||||||
|
&dummy1, &dummy2,
|
||||||
|
&exit_status,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
if (WIFEXITED (exit_status))
|
||||||
|
exit_status = WEXITSTATUS (exit_status);
|
||||||
|
|
||||||
|
g_free (dummy1);
|
||||||
|
g_free (dummy2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (exit_status == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
open_fd (Collector *collector,
|
||||||
|
GError **err)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open ("/proc/sysprof-trace", O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
load_module();
|
||||||
|
|
||||||
|
fd = open ("/proc/sysprof-trace", O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
/* FIXME: set error */
|
||||||
|
#if 0
|
||||||
|
sorry (app->main_window,
|
||||||
|
"Can't open /proc/sysprof-trace. You need to insert\n"
|
||||||
|
"the sysprof kernel module. Run\n"
|
||||||
|
"\n"
|
||||||
|
" modprobe sysprof-module\n"
|
||||||
|
"\n"
|
||||||
|
"as root.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collector->fd = fd;
|
||||||
|
fd_add_watch (collector->fd, collector);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
empty_file_descriptor (Collector *collector)
|
||||||
|
{
|
||||||
|
int rd;
|
||||||
|
SysprofStackTrace trace;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rd = read (collector->fd, &trace, sizeof (trace));
|
||||||
|
|
||||||
|
} while (rd != -1); /* until EWOULDBLOCK */
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
collector_start (Collector *collector,
|
||||||
|
GError **err)
|
||||||
|
{
|
||||||
|
if (collector->fd < 0 && !open_fd (collector, err))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
fd_set_read_callback (collector->fd, on_read);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
collector_stop (Collector *collector)
|
||||||
|
{
|
||||||
|
fd_set_read_callback (collector->fd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
collector_reset (Collector *collector)
|
||||||
|
{
|
||||||
|
if (collector->stash)
|
||||||
|
stack_stash_free (collector->stash);
|
||||||
|
|
||||||
|
collector->stash = stack_stash_new ();
|
||||||
|
collector->n_samples = 0;
|
||||||
|
|
||||||
|
g_get_current_time (&collector->latest_reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
collector_get_n_samples (Collector *collector)
|
||||||
|
{
|
||||||
|
return collector->n_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
StackStash *resolved_stash;
|
||||||
|
GHashTable *unique_symbols;
|
||||||
|
} ResolveInfo;
|
||||||
|
|
||||||
|
static char *
|
||||||
|
unique_dup (GHashTable *unique_symbols, char *s)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
result = g_hash_table_lookup (unique_symbols, s);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
result = g_strdup (s);
|
||||||
|
g_hash_table_insert (unique_symbols, s, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
lookup_symbol (Process *process, gpointer address, GHashTable *unique_symbols)
|
||||||
|
{
|
||||||
|
const Symbol *s = process_lookup_symbol (process, (gulong)address);
|
||||||
|
|
||||||
|
return unique_dup (unique_symbols, s->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
resolve_symbols (GSList *trace, gint size, gpointer data)
|
||||||
|
{
|
||||||
|
GSList *slist;
|
||||||
|
ResolveInfo *info = data;
|
||||||
|
Process *process = g_slist_last (trace)->data;
|
||||||
|
GPtrArray *resolved_trace = g_ptr_array_new ();
|
||||||
|
|
||||||
|
for (slist = trace; slist && slist->next; slist = slist->next)
|
||||||
|
{
|
||||||
|
gpointer address = slist->data;
|
||||||
|
char *symbol;
|
||||||
|
|
||||||
|
symbol = lookup_symbol (process, address, info->unique_symbols);
|
||||||
|
|
||||||
|
g_ptr_array_add (resolved_trace, symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ptr_array_add (resolved_trace,
|
||||||
|
unique_dup (info->unique_symbols,
|
||||||
|
(char *)process_get_cmdline (process)));
|
||||||
|
g_ptr_array_add (resolved_trace,
|
||||||
|
unique_dup (info->unique_symbols,
|
||||||
|
"Everything"));
|
||||||
|
|
||||||
|
stack_stash_add_trace (info->resolved_stash, (gulong *)resolved_trace->pdata, resolved_trace->len, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Profile *
|
||||||
|
collector_create_profile (Collector *collector)
|
||||||
|
{
|
||||||
|
ResolveInfo info;
|
||||||
|
|
||||||
|
info.resolved_stash = stack_stash_new ();
|
||||||
|
info.unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
|
||||||
|
stack_stash_foreach (collector->stash, resolve_symbols, &info);
|
||||||
|
|
||||||
|
g_hash_table_destroy (info.unique_symbols);
|
||||||
|
|
||||||
|
return profile_new (info.resolved_stash);
|
||||||
|
}
|
||||||
16
collector.h
Normal file
16
collector.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "profile.h"
|
||||||
|
|
||||||
|
typedef struct Collector Collector;
|
||||||
|
|
||||||
|
typedef void (* CollectorFunc) (gpointer data);
|
||||||
|
|
||||||
|
/* callback is called whenever a new sample arrives */
|
||||||
|
Collector *collector_new (CollectorFunc callback,
|
||||||
|
gpointer data);
|
||||||
|
gboolean collector_start (Collector *collector,
|
||||||
|
GError **err);
|
||||||
|
void collector_stop (Collector *collector);
|
||||||
|
void collector_reset (Collector *collector);
|
||||||
|
int collector_get_n_samples (Collector *collector);
|
||||||
|
|
||||||
|
Profile * collector_create_profile (Collector *collector);
|
||||||
56
process.c
56
process.c
@ -56,6 +56,8 @@ struct Process
|
|||||||
GList *bad_pages;
|
GList *bad_pages;
|
||||||
|
|
||||||
int pid;
|
int pid;
|
||||||
|
|
||||||
|
Symbol undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -136,7 +138,7 @@ create_process (const char *cmdline, int pid)
|
|||||||
{
|
{
|
||||||
Process *p;
|
Process *p;
|
||||||
|
|
||||||
p = g_new (Process, 1);
|
p = g_new0 (Process, 1);
|
||||||
|
|
||||||
if (*cmdline != '\0')
|
if (*cmdline != '\0')
|
||||||
p->cmdline = g_strdup_printf ("[%s]", cmdline);
|
p->cmdline = g_strdup_printf ("[%s]", cmdline);
|
||||||
@ -146,6 +148,8 @@ create_process (const char *cmdline, int pid)
|
|||||||
p->bad_pages = NULL;
|
p->bad_pages = NULL;
|
||||||
p->maps = NULL;
|
p->maps = NULL;
|
||||||
p->pid = pid;
|
p->pid = pid;
|
||||||
|
p->undefined.name = NULL;
|
||||||
|
p->undefined.address = NULL;
|
||||||
|
|
||||||
g_assert (!g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid)));
|
g_assert (!g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid)));
|
||||||
g_assert (!g_hash_table_lookup (processes_by_cmdline, cmdline));
|
g_assert (!g_hash_table_lookup (processes_by_cmdline, cmdline));
|
||||||
@ -340,7 +344,6 @@ process_get_from_pid (int pid)
|
|||||||
const Symbol *
|
const Symbol *
|
||||||
process_lookup_symbol (Process *process, gulong address)
|
process_lookup_symbol (Process *process, gulong address)
|
||||||
{
|
{
|
||||||
static Symbol undefined;
|
|
||||||
const Symbol *result;
|
const Symbol *result;
|
||||||
static Symbol kernel;
|
static Symbol kernel;
|
||||||
Map *map = process_locate_map (process, address);
|
Map *map = process_locate_map (process, address);
|
||||||
@ -349,18 +352,20 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
|
|
||||||
if (address == 0x1)
|
if (address == 0x1)
|
||||||
{
|
{
|
||||||
kernel.name = "in kernel";
|
kernel.name = "In kernel";
|
||||||
kernel.address = 0x00001337;
|
kernel.address = 0x00001337;
|
||||||
return &kernel;
|
return &kernel;
|
||||||
}
|
}
|
||||||
else if (!map)
|
else if (!map)
|
||||||
{
|
{
|
||||||
if (undefined.name)
|
if (!process->undefined.name)
|
||||||
g_free (undefined.name);
|
{
|
||||||
undefined.name = g_strdup_printf ("??? %s", process->cmdline);
|
process->undefined.name =
|
||||||
undefined.address = 0xBABE0001;
|
g_strdup_printf ("(??? %s)", process->cmdline);
|
||||||
|
process->undefined.address = 0xBABE0001;
|
||||||
|
}
|
||||||
|
|
||||||
return &undefined;
|
return &process->undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
address -= map->start;
|
address -= map->start;
|
||||||
@ -388,38 +393,3 @@ process_get_cmdline (Process *process)
|
|||||||
{
|
{
|
||||||
return process->cmdline;
|
return process->cmdline;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
free_process (gpointer key, gpointer value, gpointer data)
|
|
||||||
{
|
|
||||||
char *cmdline = key;
|
|
||||||
Process *process = value;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_print ("freeing: %p\n", process);
|
|
||||||
memset (process, '\0', sizeof (Process));
|
|
||||||
#endif
|
|
||||||
g_free (process->cmdline);
|
|
||||||
#if 0
|
|
||||||
process->cmdline = "You are using free()'d memory";
|
|
||||||
#endif
|
|
||||||
process_free_maps (process);
|
|
||||||
g_list_free (process->bad_pages);
|
|
||||||
g_free (cmdline);
|
|
||||||
|
|
||||||
g_free (process);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
process_flush_caches (void)
|
|
||||||
{
|
|
||||||
if (!processes_by_cmdline)
|
|
||||||
return;
|
|
||||||
g_hash_table_foreach (processes_by_cmdline, free_process, NULL);
|
|
||||||
|
|
||||||
g_hash_table_destroy (processes_by_cmdline);
|
|
||||||
g_hash_table_destroy (processes_by_pid);
|
|
||||||
|
|
||||||
processes_by_cmdline = NULL;
|
|
||||||
processes_by_pid = NULL;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
#ifndef PROCESS_H
|
#ifndef PROCESS_H
|
||||||
#define PROCESS_H
|
#define PROCESS_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
#include "binfile.h"
|
#include "binfile.h"
|
||||||
|
|
||||||
typedef struct Process Process;
|
typedef struct Process Process;
|
||||||
@ -41,9 +42,11 @@ typedef struct Process Process;
|
|||||||
* To flush the pid cache, call process_flush_caches().
|
* To flush the pid cache, call process_flush_caches().
|
||||||
* This will invalidate all instances of Process.
|
* This will invalidate all instances of Process.
|
||||||
*
|
*
|
||||||
|
* The real fix for this is probably to have the kernel module report the
|
||||||
|
* maps along with the stacktrace.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void process_flush_caches (void);
|
|
||||||
Process * process_get_from_pid (int pid);
|
Process * process_get_from_pid (int pid);
|
||||||
void process_ensure_map (Process *process,
|
void process_ensure_map (Process *process,
|
||||||
int pid,
|
int pid,
|
||||||
|
|||||||
548
profile.c
548
profile.c
@ -38,37 +38,9 @@ update()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
|
||||||
direct_hash_no_null (gconstpointer v)
|
|
||||||
{
|
|
||||||
g_assert (v != NULL);
|
|
||||||
return GPOINTER_TO_UINT (v);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node
|
|
||||||
{
|
|
||||||
ProfileObject *object;
|
|
||||||
|
|
||||||
Node *siblings; /* siblings in the call tree */
|
|
||||||
Node *children; /* children in the call tree */
|
|
||||||
Node *parent; /* parent in call tree */
|
|
||||||
Node *next; /* nodes that correspond to same object are linked though
|
|
||||||
* this pointer
|
|
||||||
*/
|
|
||||||
|
|
||||||
guint total;
|
|
||||||
guint self;
|
|
||||||
|
|
||||||
gboolean toplevel;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Profile
|
struct Profile
|
||||||
{
|
{
|
||||||
gint size;
|
StackStash * stash;
|
||||||
Node * call_tree;
|
|
||||||
|
|
||||||
/* This table is really a cache. We can build it from the call_tree */
|
|
||||||
GHashTable * nodes_by_object;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static SFormat *
|
static SFormat *
|
||||||
@ -105,34 +77,52 @@ create_format (void)
|
|||||||
NULL));
|
NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
add_object (gpointer key, gpointer value, gpointer data)
|
sum_children (StackNode *node)
|
||||||
{
|
{
|
||||||
SFileOutput *output = data;
|
int total;
|
||||||
ProfileObject *object = key;
|
StackNode *child;
|
||||||
|
|
||||||
sfile_begin_add_record (output, "object");
|
|
||||||
|
|
||||||
sfile_add_string (output, "name", object->name);
|
|
||||||
sfile_add_integer (output, "total", object->total);
|
|
||||||
sfile_add_integer (output, "self", object->self);
|
|
||||||
|
|
||||||
sfile_end_add (output, "object", object);
|
/* FIXME: this is pretty inefficient. Instead perhaps
|
||||||
|
* maintain or compute it in the stackstash
|
||||||
|
*/
|
||||||
|
total = node->size;
|
||||||
|
|
||||||
|
for (child = node->children; child != NULL; child = child->siblings)
|
||||||
|
total += sum_children (child);
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compute_total (StackNode *node)
|
||||||
|
{
|
||||||
|
StackNode *n;
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
for (n = node; n != NULL; n = n->next)
|
||||||
|
{
|
||||||
|
if (n->toplevel)
|
||||||
|
total += sum_children (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
serialize_call_tree (Node *node, SFileOutput *output)
|
serialize_call_tree (StackNode *node,
|
||||||
|
SFileOutput *output)
|
||||||
{
|
{
|
||||||
if (!node)
|
if (!node)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sfile_begin_add_record (output, "node");
|
sfile_begin_add_record (output, "node");
|
||||||
sfile_add_pointer (output, "object", node->object);
|
sfile_add_pointer (output, "object", node->address);
|
||||||
sfile_add_pointer (output, "siblings", node->siblings);
|
sfile_add_pointer (output, "siblings", node->siblings);
|
||||||
sfile_add_pointer (output, "children", node->children);
|
sfile_add_pointer (output, "children", node->children);
|
||||||
sfile_add_pointer (output, "parent", node->parent);
|
sfile_add_pointer (output, "parent", node->parent);
|
||||||
sfile_add_integer (output, "total", node->total);
|
sfile_add_integer (output, "total", compute_total (node));
|
||||||
sfile_add_integer (output, "self", node->self);
|
sfile_add_integer (output, "self", node->size);
|
||||||
sfile_add_integer (output, "toplevel", node->toplevel);
|
sfile_add_integer (output, "toplevel", node->toplevel);
|
||||||
sfile_end_add (output, "node", node);
|
sfile_end_add (output, "node", node);
|
||||||
|
|
||||||
@ -146,21 +136,40 @@ profile_save (Profile *profile,
|
|||||||
GError **err)
|
GError **err)
|
||||||
{
|
{
|
||||||
gboolean result;
|
gboolean result;
|
||||||
|
|
||||||
|
GList *profile_objects;
|
||||||
|
GList *list;
|
||||||
|
|
||||||
SFormat *format = create_format ();
|
SFormat *format = create_format ();
|
||||||
SFileOutput *output = sfile_output_new (format);
|
SFileOutput *output = sfile_output_new (format);
|
||||||
|
|
||||||
sfile_begin_add_record (output, "profile");
|
sfile_begin_add_record (output, "profile");
|
||||||
|
|
||||||
sfile_add_integer (output, "size", profile->size);
|
sfile_add_integer (output, "size", profile_get_size (profile));
|
||||||
sfile_add_pointer (output, "call_tree", profile->call_tree);
|
sfile_add_pointer (output, "call_tree",
|
||||||
|
stack_stash_get_root (profile->stash));
|
||||||
|
|
||||||
|
profile_objects = profile_get_objects (profile);
|
||||||
sfile_begin_add_list (output, "objects");
|
sfile_begin_add_list (output, "objects");
|
||||||
g_hash_table_foreach (profile->nodes_by_object, add_object, output);
|
for (list = profile_objects; list != NULL; list = list->next)
|
||||||
|
{
|
||||||
|
ProfileObject *object = list->data;
|
||||||
|
|
||||||
|
sfile_begin_add_record (output, "object");
|
||||||
|
|
||||||
|
sfile_add_string (output, "name", object->name);
|
||||||
|
sfile_add_integer (output, "total", object->total);
|
||||||
|
sfile_add_integer (output, "self", object->self);
|
||||||
|
|
||||||
|
sfile_end_add (output, "object", object->name);
|
||||||
|
}
|
||||||
|
g_list_foreach (profile_objects, (GFunc)g_free, NULL);
|
||||||
|
g_list_free (profile_objects);
|
||||||
|
|
||||||
sfile_end_add (output, "objects", NULL);
|
sfile_end_add (output, "objects", NULL);
|
||||||
|
|
||||||
sfile_begin_add_list (output, "nodes");
|
sfile_begin_add_list (output, "nodes");
|
||||||
serialize_call_tree (profile->call_tree, output);
|
serialize_call_tree (stack_stash_get_root (profile->stash), output);
|
||||||
sfile_end_add (output, "nodes", NULL);
|
sfile_end_add (output, "nodes", NULL);
|
||||||
|
|
||||||
sfile_end_add (output, "profile", NULL);
|
sfile_end_add (output, "profile", NULL);
|
||||||
@ -173,6 +182,7 @@ profile_save (Profile *profile,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static void
|
static void
|
||||||
make_hash_table (Node *node, GHashTable *table)
|
make_hash_table (Node *node, GHashTable *table)
|
||||||
{
|
{
|
||||||
@ -189,6 +199,7 @@ make_hash_table (Node *node, GHashTable *table)
|
|||||||
make_hash_table (node->siblings, table);
|
make_hash_table (node->siblings, table);
|
||||||
make_hash_table (node->children, table);
|
make_hash_table (node->children, table);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Profile *
|
Profile *
|
||||||
profile_load (const char *filename, GError **err)
|
profile_load (const char *filename, GError **err)
|
||||||
@ -197,52 +208,49 @@ profile_load (const char *filename, GError **err)
|
|||||||
SFileInput *input;
|
SFileInput *input;
|
||||||
Profile *profile;
|
Profile *profile;
|
||||||
int n, i;
|
int n, i;
|
||||||
|
StackNode *root;
|
||||||
|
|
||||||
format = create_format ();
|
format = create_format ();
|
||||||
input = sfile_load (filename, format, err);
|
input = sfile_load (filename, format, err);
|
||||||
|
|
||||||
if (!input)
|
if (!input)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
profile = g_new (Profile, 1);
|
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_begin_get_record (input, "profile");
|
||||||
|
|
||||||
sfile_get_integer (input, "size", &profile->size);
|
sfile_get_integer (input, "size", NULL);
|
||||||
sfile_get_pointer (input, "call_tree", (void **)&profile->call_tree);
|
sfile_get_pointer (input, "call_tree", (gpointer *)&root);
|
||||||
|
|
||||||
n = sfile_begin_get_list (input, "objects");
|
n = sfile_begin_get_list (input, "objects");
|
||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
ProfileObject *obj = g_new (ProfileObject, 1);
|
char *string;
|
||||||
|
|
||||||
sfile_begin_get_record (input, "object");
|
sfile_begin_get_record (input, "object");
|
||||||
|
|
||||||
sfile_get_string (input, "name", &obj->name);
|
sfile_get_string (input, "name", &string);
|
||||||
sfile_get_integer (input, "total", (gint32 *)&obj->total);
|
sfile_get_integer (input, "total", NULL);
|
||||||
sfile_get_integer (input, "self", (gint32 *)&obj->self);
|
sfile_get_integer (input, "self", NULL);
|
||||||
|
|
||||||
sfile_end_get (input, "object", obj);
|
sfile_end_get (input, "object", string);
|
||||||
}
|
}
|
||||||
sfile_end_get (input, "objects", NULL);
|
sfile_end_get (input, "objects", NULL);
|
||||||
|
|
||||||
profile->call_tree = NULL;
|
|
||||||
n = sfile_begin_get_list (input, "nodes");
|
n = sfile_begin_get_list (input, "nodes");
|
||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
Node *node = g_new (Node, 1);
|
StackNode *node = g_new (StackNode, 1);
|
||||||
|
|
||||||
sfile_begin_get_record (input, "node");
|
sfile_begin_get_record (input, "node");
|
||||||
|
|
||||||
sfile_get_pointer (input, "object", (gpointer *)&node->object);
|
sfile_get_pointer (input, "object", (gpointer *)&node->address);
|
||||||
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
|
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
|
||||||
sfile_get_pointer (input, "children", (gpointer *)&node->children);
|
sfile_get_pointer (input, "children", (gpointer *)&node->children);
|
||||||
sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
|
sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
|
||||||
sfile_get_integer (input, "total", (gint32 *)&node->total);
|
sfile_get_integer (input, "total", NULL);
|
||||||
sfile_get_integer (input, "self", (gint32 *)&node->self);
|
sfile_get_integer (input, "self", (gint32 *)&node->size);
|
||||||
sfile_get_integer (input, "toplevel", &node->toplevel);
|
sfile_get_integer (input, "toplevel", &node->toplevel);
|
||||||
|
|
||||||
sfile_end_get (input, "node", node);
|
sfile_end_get (input, "node", node);
|
||||||
@ -254,285 +262,20 @@ profile_load (const char *filename, GError **err)
|
|||||||
|
|
||||||
sformat_free (format);
|
sformat_free (format);
|
||||||
sfile_input_free (input);
|
sfile_input_free (input);
|
||||||
|
|
||||||
make_hash_table (profile->call_tree, profile->nodes_by_object);
|
|
||||||
|
|
||||||
|
profile->stash = stack_stash_new_from_root (root);
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ProfileObject *
|
|
||||||
profile_object_new (void)
|
|
||||||
{
|
|
||||||
ProfileObject *obj = g_new (ProfileObject, 1);
|
|
||||||
obj->total = 0;
|
|
||||||
obj->self = 0;
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
profile_object_free (ProfileObject *obj)
|
|
||||||
{
|
|
||||||
g_free (obj->name);
|
|
||||||
g_free (obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *
|
|
||||||
generate_key (Process *process, gulong address)
|
|
||||||
{
|
|
||||||
if (address)
|
|
||||||
{
|
|
||||||
const Symbol *symbol = process_lookup_symbol (process, address);
|
|
||||||
|
|
||||||
return g_strdup_printf ("%p%s", (void *)symbol->address, symbol->name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return g_strdup_printf ("p:%p", process_get_cmdline (process));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *
|
|
||||||
generate_presentation_name (Process *process, gulong address)
|
|
||||||
{
|
|
||||||
/* FIXME - not10
|
|
||||||
* using 0 to indicate "process" is broken
|
|
||||||
*/
|
|
||||||
if (address)
|
|
||||||
{
|
|
||||||
const Symbol *symbol = process_lookup_symbol (process, address);
|
|
||||||
|
|
||||||
return g_strdup_printf ("%s", symbol->name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return g_strdup_printf ("%s", process_get_cmdline (process));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ensure_profile_object (GHashTable *profile_objects, Process *process, gulong address)
|
|
||||||
{
|
|
||||||
char *key = generate_key (process, address);
|
|
||||||
|
|
||||||
if (!g_hash_table_lookup (profile_objects, key))
|
|
||||||
{
|
|
||||||
ProfileObject *object;
|
|
||||||
|
|
||||||
object = profile_object_new ();
|
|
||||||
object->name = generate_presentation_name (process, address);
|
|
||||||
|
|
||||||
g_hash_table_insert (profile_objects, key, object);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_free (key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ProfileObject *
|
|
||||||
lookup_profile_object (GHashTable *profile_objects, Process *process, gulong address)
|
|
||||||
{
|
|
||||||
ProfileObject *object;
|
|
||||||
char *key = generate_key (process, address);
|
|
||||||
object = g_hash_table_lookup (profile_objects, key);
|
|
||||||
g_free (key);
|
|
||||||
g_assert (object);
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct Info Info;
|
|
||||||
struct Info
|
|
||||||
{
|
|
||||||
Profile *profile;
|
|
||||||
GHashTable *profile_objects;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
generate_object_table (Process *process, GSList *trace, gint size, gpointer data)
|
|
||||||
{
|
|
||||||
Info *info = data;
|
|
||||||
GSList *list;
|
|
||||||
|
|
||||||
ensure_profile_object (info->profile_objects, process, 0);
|
|
||||||
|
|
||||||
for (list = trace; list != NULL; list = list->next)
|
|
||||||
{
|
|
||||||
update ();
|
|
||||||
ensure_profile_object (info->profile_objects, process, (gulong)list->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
info->profile->size += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Node *
|
|
||||||
node_new ()
|
|
||||||
{
|
|
||||||
Node *node = g_new (Node, 1);
|
|
||||||
node->siblings = NULL;
|
|
||||||
node->children = NULL;
|
|
||||||
node->parent = NULL;
|
|
||||||
node->next = NULL;
|
|
||||||
node->object = NULL;
|
|
||||||
node->self = 0;
|
|
||||||
node->total = 0;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Node *
|
|
||||||
node_add_trace (Profile *profile, GHashTable *profile_objects, Node *node, Process *process,
|
|
||||||
GSList *trace, gint size,
|
|
||||||
GHashTable *seen_objects)
|
|
||||||
{
|
|
||||||
ProfileObject *object;
|
|
||||||
Node *match = NULL;
|
|
||||||
|
|
||||||
if (!trace)
|
|
||||||
return node;
|
|
||||||
|
|
||||||
object = lookup_profile_object (profile_objects, process, (gulong)trace->data);
|
|
||||||
for (match = node; match != NULL; match = match->siblings)
|
|
||||||
{
|
|
||||||
if (match->object == object)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match)
|
|
||||||
{
|
|
||||||
match = node_new ();
|
|
||||||
match->object = object;
|
|
||||||
match->siblings = node;
|
|
||||||
node = match;
|
|
||||||
|
|
||||||
if (g_hash_table_lookup (seen_objects, object))
|
|
||||||
match->toplevel = FALSE;
|
|
||||||
else
|
|
||||||
match->toplevel = TRUE;
|
|
||||||
|
|
||||||
match->next = g_hash_table_lookup (profile->nodes_by_object, object);
|
|
||||||
g_hash_table_insert (profile->nodes_by_object, object, match);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_hash_table_insert (seen_objects, object, GINT_TO_POINTER (1));
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
g_print ("%s adds %d\n", match->object->name, size);
|
|
||||||
#endif
|
|
||||||
match->total += size;
|
|
||||||
if (!trace->next)
|
|
||||||
match->self += size;
|
|
||||||
|
|
||||||
match->children = node_add_trace (profile, profile_objects, match->children, process, trace->next, size,
|
|
||||||
seen_objects);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void
|
|
||||||
dump_trace (GSList *trace)
|
|
||||||
{
|
|
||||||
g_print ("TRACE: ");
|
|
||||||
while (trace)
|
|
||||||
{
|
|
||||||
g_print ("%x ", trace->data);
|
|
||||||
trace = trace->next;
|
|
||||||
}
|
|
||||||
g_print ("\n\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
|
||||||
generate_call_tree (Process *process, GSList *trace, gint size, gpointer data)
|
|
||||||
{
|
|
||||||
Info *info = data;
|
|
||||||
Node *match = NULL;
|
|
||||||
ProfileObject *proc = lookup_profile_object (info->profile_objects, process, 0);
|
|
||||||
GHashTable *seen_objects;
|
|
||||||
|
|
||||||
for (match = info->profile->call_tree; match; match = match->siblings)
|
|
||||||
{
|
|
||||||
if (match->object == proc)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match)
|
|
||||||
{
|
|
||||||
match = node_new ();
|
|
||||||
match->object = proc;
|
|
||||||
match->siblings = info->profile->call_tree;
|
|
||||||
info->profile->call_tree = match;
|
|
||||||
match->toplevel = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_hash_table_insert (info->profile->nodes_by_object, proc, match);
|
|
||||||
|
|
||||||
match->total += size;
|
|
||||||
if (!trace)
|
|
||||||
match->self += size;
|
|
||||||
|
|
||||||
seen_objects = g_hash_table_new (direct_hash_no_null, g_direct_equal);
|
|
||||||
|
|
||||||
g_hash_table_insert (seen_objects, proc, GINT_TO_POINTER (1));
|
|
||||||
|
|
||||||
update ();
|
|
||||||
match->children = node_add_trace (info->profile, info->profile_objects, match->children, process,
|
|
||||||
trace, size, seen_objects);
|
|
||||||
|
|
||||||
g_hash_table_destroy (seen_objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
link_parents (Node *node, Node *parent)
|
|
||||||
{
|
|
||||||
if (!node)
|
|
||||||
return;
|
|
||||||
|
|
||||||
node->parent = parent;
|
|
||||||
|
|
||||||
link_parents (node->siblings, parent);
|
|
||||||
link_parents (node->children, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
compute_object_total (gpointer key, gpointer value, gpointer data)
|
|
||||||
{
|
|
||||||
Node *node;
|
|
||||||
ProfileObject *object = key;
|
|
||||||
|
|
||||||
for (node = value; node != NULL; node = node->next)
|
|
||||||
{
|
|
||||||
object->self += node->self;
|
|
||||||
if (node->toplevel)
|
|
||||||
object->total += node->total;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Profile *
|
Profile *
|
||||||
profile_new (StackStash *stash)
|
profile_new (StackStash *stash)
|
||||||
{
|
{
|
||||||
Info info;
|
Profile *profile = g_new (Profile, 1);
|
||||||
|
|
||||||
info.profile = g_new (Profile, 1);
|
profile->stash = stash;
|
||||||
info.profile->call_tree = NULL;
|
|
||||||
info.profile->nodes_by_object =
|
|
||||||
g_hash_table_new (direct_hash_no_null, g_direct_equal);
|
|
||||||
info.profile->size = 0;
|
|
||||||
|
|
||||||
/* profile objects */
|
return profile;
|
||||||
info.profile_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
||||||
g_free, NULL);
|
|
||||||
|
|
||||||
stack_stash_foreach (stash, generate_object_table, &info);
|
|
||||||
stack_stash_foreach (stash, generate_call_tree, &info);
|
|
||||||
link_parents (info.profile->call_tree, NULL);
|
|
||||||
|
|
||||||
g_hash_table_foreach (info.profile->nodes_by_object, compute_object_total, NULL);
|
|
||||||
|
|
||||||
g_hash_table_destroy (info.profile_objects);
|
|
||||||
|
|
||||||
return info.profile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -548,14 +291,14 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
|
|||||||
|
|
||||||
for (list = trace; list != NULL; list = list->next)
|
for (list = trace; list != NULL; list = list->next)
|
||||||
{
|
{
|
||||||
Node *node = list->data;
|
StackNode *node = list->data;
|
||||||
ProfileDescendant *match = NULL;
|
ProfileDescendant *match = NULL;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
for (match = *tree; match != NULL; match = match->siblings)
|
for (match = *tree; match != NULL; match = match->siblings)
|
||||||
{
|
{
|
||||||
if (match->object == node->object)
|
if (match->name == node->address)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +311,7 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
|
|||||||
for (i = 0; i < seen_objects->len; ++i)
|
for (i = 0; i < seen_objects->len; ++i)
|
||||||
{
|
{
|
||||||
ProfileDescendant *n = seen_objects->pdata[i];
|
ProfileDescendant *n = seen_objects->pdata[i];
|
||||||
if (n->object == node->object)
|
if (n->name == node->address)
|
||||||
seen_tree_node = n;
|
seen_tree_node = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +339,7 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
|
|||||||
{
|
{
|
||||||
match = g_new (ProfileDescendant, 1);
|
match = g_new (ProfileDescendant, 1);
|
||||||
|
|
||||||
match->object = node->object;
|
match->name = node->address;
|
||||||
match->non_recursion = 0;
|
match->non_recursion = 0;
|
||||||
match->total = 0;
|
match->total = 0;
|
||||||
match->self = 0;
|
match->self = 0;
|
||||||
@ -656,57 +399,45 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
node_list_leaves (Node *node, GList **leaves)
|
add_leaf_to_tree (ProfileDescendant **tree, StackNode *leaf, StackNode *top)
|
||||||
{
|
|
||||||
Node *n;
|
|
||||||
|
|
||||||
if (node->self > 0)
|
|
||||||
*leaves = g_list_prepend (*leaves, node);
|
|
||||||
|
|
||||||
for (n = node->children; n != NULL; n = n->siblings)
|
|
||||||
node_list_leaves (n, leaves);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_leaf_to_tree (ProfileDescendant **tree, Node *leaf, Node *top)
|
|
||||||
{
|
{
|
||||||
GList *trace = NULL;
|
GList *trace = NULL;
|
||||||
Node *node;
|
StackNode *node;
|
||||||
|
|
||||||
for (node = leaf; node != top->parent; node = node->parent)
|
for (node = leaf; node != top->parent; node = node->parent)
|
||||||
trace = g_list_prepend (trace, node);
|
trace = g_list_prepend (trace, node);
|
||||||
|
|
||||||
add_trace_to_tree (tree, trace, leaf->self);
|
add_trace_to_tree (tree, trace, leaf->size);
|
||||||
|
|
||||||
g_list_free (trace);
|
g_list_free (trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileDescendant *
|
ProfileDescendant *
|
||||||
profile_create_descendants (Profile *profile, ProfileObject *object)
|
profile_create_descendants (Profile *profile,
|
||||||
|
char *object_name)
|
||||||
{
|
{
|
||||||
ProfileDescendant *tree = NULL;
|
ProfileDescendant *tree = NULL;
|
||||||
Node *node;
|
|
||||||
|
|
||||||
node = g_hash_table_lookup (profile->nodes_by_object, object);
|
|
||||||
|
|
||||||
|
StackNode *node = stack_stash_find_node (profile->stash, object_name);
|
||||||
|
|
||||||
while (node)
|
while (node)
|
||||||
{
|
{
|
||||||
update();
|
|
||||||
if (node->toplevel)
|
if (node->toplevel)
|
||||||
{
|
{
|
||||||
GList *leaves = NULL;
|
GList *leaves = NULL;
|
||||||
GList *list;
|
GList *list;
|
||||||
|
|
||||||
node_list_leaves (node, &leaves);
|
stack_node_list_leaves (node, &leaves);
|
||||||
|
|
||||||
for (list = leaves; list != NULL; list = list->next)
|
for (list = leaves; list != NULL; list = list->next)
|
||||||
add_leaf_to_tree (&tree, list->data, node);
|
add_leaf_to_tree (&tree, list->data, node);
|
||||||
|
|
||||||
g_list_free (leaves);
|
g_list_free (leaves);
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,32 +453,33 @@ profile_caller_new (void)
|
|||||||
|
|
||||||
ProfileCaller *
|
ProfileCaller *
|
||||||
profile_list_callers (Profile *profile,
|
profile_list_callers (Profile *profile,
|
||||||
ProfileObject *callee)
|
char *callee_name)
|
||||||
{
|
{
|
||||||
Node *callee_node;
|
StackNode *callee_node;
|
||||||
Node *node;
|
StackNode *node;
|
||||||
GHashTable *callers_by_object;
|
GHashTable *callers_by_object;
|
||||||
GHashTable *seen_callers;
|
GHashTable *seen_callers;
|
||||||
ProfileCaller *result = NULL;
|
ProfileCaller *result = NULL;
|
||||||
|
|
||||||
callers_by_object =
|
callers_by_object =
|
||||||
g_hash_table_new (g_direct_hash, g_direct_equal);
|
g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
seen_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
|
seen_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
|
||||||
callee_node = g_hash_table_lookup (profile->nodes_by_object, callee);
|
callee_node = stack_stash_find_node (profile->stash, callee_name);
|
||||||
|
|
||||||
for (node = callee_node; node; node = node->next)
|
for (node = callee_node; node; node = node->next)
|
||||||
{
|
{
|
||||||
ProfileObject *object;
|
char *object;
|
||||||
|
|
||||||
if (node->parent)
|
if (node->parent)
|
||||||
object = node->parent->object;
|
object = node->parent->address;
|
||||||
else
|
else
|
||||||
object = NULL;
|
object = NULL;
|
||||||
|
|
||||||
if (!g_hash_table_lookup (callers_by_object, object))
|
if (!g_hash_table_lookup (callers_by_object, object))
|
||||||
{
|
{
|
||||||
ProfileCaller *caller = profile_caller_new ();
|
ProfileCaller *caller = profile_caller_new ();
|
||||||
caller->object = object;
|
caller->name = object;
|
||||||
g_hash_table_insert (callers_by_object, object, caller);
|
g_hash_table_insert (callers_by_object, object, caller);
|
||||||
|
|
||||||
caller->next = result;
|
caller->next = result;
|
||||||
@ -757,14 +489,14 @@ profile_list_callers (Profile *profile,
|
|||||||
|
|
||||||
for (node = callee_node; node != NULL; node = node->next)
|
for (node = callee_node; node != NULL; node = node->next)
|
||||||
{
|
{
|
||||||
Node *top_caller;
|
StackNode *top_caller;
|
||||||
Node *top_callee;
|
StackNode *top_callee;
|
||||||
Node *n;
|
StackNode *n;
|
||||||
ProfileCaller *caller;
|
ProfileCaller *caller;
|
||||||
ProfileObject *object;
|
char *object;
|
||||||
|
|
||||||
if (node->parent)
|
if (node->parent)
|
||||||
object = node->parent->object;
|
object = node->parent->address;
|
||||||
else
|
else
|
||||||
object = NULL;
|
object = NULL;
|
||||||
|
|
||||||
@ -775,8 +507,8 @@ profile_list_callers (Profile *profile,
|
|||||||
top_callee = node;
|
top_callee = node;
|
||||||
for (n = node; n && n->parent; n = n->parent)
|
for (n = node; n && n->parent; n = n->parent)
|
||||||
{
|
{
|
||||||
if (n->object == node->object &&
|
if (n->address == node->address &&
|
||||||
n->parent->object == node->parent->object)
|
n->parent->address == node->parent->address)
|
||||||
{
|
{
|
||||||
top_caller = n->parent;
|
top_caller = n->parent;
|
||||||
top_callee = n;
|
top_callee = n;
|
||||||
@ -785,13 +517,13 @@ profile_list_callers (Profile *profile,
|
|||||||
|
|
||||||
if (!g_hash_table_lookup (seen_callers, top_caller))
|
if (!g_hash_table_lookup (seen_callers, top_caller))
|
||||||
{
|
{
|
||||||
caller->total += top_callee->total;
|
caller->total += compute_total (top_callee);
|
||||||
|
|
||||||
g_hash_table_insert (seen_callers, top_caller, (void *)0x1);
|
g_hash_table_insert (seen_callers, top_caller, (void *)0x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->self > 0)
|
if (node->size > 0)
|
||||||
caller->self += node->self;
|
caller->self += node->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_destroy (seen_callers);
|
g_hash_table_destroy (seen_callers);
|
||||||
@ -801,32 +533,10 @@ profile_list_callers (Profile *profile,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
node_free (Node *node)
|
|
||||||
{
|
|
||||||
if (!node)
|
|
||||||
return;
|
|
||||||
|
|
||||||
node_free (node->siblings);
|
|
||||||
node_free (node->children);
|
|
||||||
g_free (node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
free_object (gpointer key, gpointer value, gpointer data)
|
|
||||||
{
|
|
||||||
profile_object_free (key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
profile_free (Profile *profile)
|
profile_free (Profile *profile)
|
||||||
{
|
{
|
||||||
g_hash_table_foreach (profile->nodes_by_object, free_object, NULL);
|
/* FIXME unref stash */
|
||||||
|
|
||||||
node_free (profile->call_tree);
|
|
||||||
|
|
||||||
g_hash_table_destroy (profile->nodes_by_object);
|
|
||||||
|
|
||||||
g_free (profile);
|
g_free (profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,20 +563,34 @@ profile_caller_free (ProfileCaller *caller)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
build_object_list (gpointer key, gpointer value, gpointer data)
|
build_object_list (StackNode *node, gpointer data)
|
||||||
{
|
{
|
||||||
ProfileObject *object = key;
|
|
||||||
GList **objects = data;
|
GList **objects = data;
|
||||||
|
ProfileObject *obj;
|
||||||
*objects = g_list_prepend (*objects, object);
|
|
||||||
|
obj = g_new (ProfileObject, 1);
|
||||||
|
obj->name = node->address;
|
||||||
|
|
||||||
|
obj->total = compute_total (node);
|
||||||
|
|
||||||
|
/* FIXME: this is incorrect. We need to sum all the node linked
|
||||||
|
* through node->next
|
||||||
|
*/
|
||||||
|
obj->self = node->size;
|
||||||
|
|
||||||
|
*objects = g_list_prepend (*objects, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
GList *
|
GList *
|
||||||
profile_get_objects (Profile *profile)
|
profile_get_objects (Profile *profile)
|
||||||
{
|
{
|
||||||
GList *objects = NULL;
|
GList *objects = NULL;
|
||||||
|
|
||||||
g_hash_table_foreach (profile->nodes_by_object, build_object_list, &objects);
|
stack_stash_foreach_by_address (profile->stash, build_object_list, &objects);
|
||||||
|
|
||||||
|
/* FIXME: everybody still assumes that they don't have to free the
|
||||||
|
* objects in the list, but these days they do, and so we are leaking.
|
||||||
|
*/
|
||||||
|
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
@ -874,5 +598,5 @@ profile_get_objects (Profile *profile)
|
|||||||
gint
|
gint
|
||||||
profile_get_size (Profile *profile)
|
profile_get_size (Profile *profile)
|
||||||
{
|
{
|
||||||
return profile->size;
|
return compute_total (stack_stash_get_root (profile->stash));
|
||||||
}
|
}
|
||||||
|
|||||||
15
profile.h
15
profile.h
@ -17,9 +17,10 @@
|
|||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef PROFILE_H
|
||||||
|
#define PROFILE_H
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "binfile.h"
|
|
||||||
#include "process.h"
|
|
||||||
#include "stackstash.h"
|
#include "stackstash.h"
|
||||||
|
|
||||||
typedef struct Profile Profile;
|
typedef struct Profile Profile;
|
||||||
@ -37,7 +38,7 @@ struct ProfileObject
|
|||||||
|
|
||||||
struct ProfileDescendant
|
struct ProfileDescendant
|
||||||
{
|
{
|
||||||
ProfileObject * object;
|
char * name;
|
||||||
guint self;
|
guint self;
|
||||||
guint total;
|
guint total;
|
||||||
guint non_recursion;
|
guint non_recursion;
|
||||||
@ -51,7 +52,7 @@ struct ProfileDescendant
|
|||||||
|
|
||||||
struct ProfileCaller
|
struct ProfileCaller
|
||||||
{
|
{
|
||||||
ProfileObject * object; /* can be NULL */
|
char * name;
|
||||||
guint total;
|
guint total;
|
||||||
guint self;
|
guint self;
|
||||||
|
|
||||||
@ -63,9 +64,9 @@ void profile_free (Profile *profile);
|
|||||||
gint profile_get_size (Profile *profile);
|
gint profile_get_size (Profile *profile);
|
||||||
GList * profile_get_objects (Profile *profile);
|
GList * profile_get_objects (Profile *profile);
|
||||||
ProfileDescendant *profile_create_descendants (Profile *prf,
|
ProfileDescendant *profile_create_descendants (Profile *prf,
|
||||||
ProfileObject *object);
|
char *object);
|
||||||
ProfileCaller * profile_list_callers (Profile *profile,
|
ProfileCaller * profile_list_callers (Profile *profile,
|
||||||
ProfileObject *callee);
|
char *object);
|
||||||
void profile_caller_free (ProfileCaller *caller);
|
void profile_caller_free (ProfileCaller *caller);
|
||||||
void profile_descendant_free (ProfileDescendant *descendant);
|
void profile_descendant_free (ProfileDescendant *descendant);
|
||||||
|
|
||||||
@ -74,3 +75,5 @@ gboolean profile_save (Profile *profile,
|
|||||||
GError **err);
|
GError **err);
|
||||||
Profile * profile_load (const char *filename,
|
Profile * profile_load (const char *filename,
|
||||||
GError **err);
|
GError **err);
|
||||||
|
|
||||||
|
#endif /* PROFILE_H */
|
||||||
|
|||||||
22
sfile.h
22
sfile.h
@ -23,6 +23,24 @@ typedef struct SFileOutput SFileOutput;
|
|||||||
typedef guint SType;
|
typedef guint SType;
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
Serializer *serializer_new (const char *version);
|
||||||
|
void serializer_set_format (Serializer *serializer,
|
||||||
|
SerializerFormat *format);
|
||||||
|
SerializerFormat *serializer_make_list (Serializer *serializer,
|
||||||
|
const char *name,
|
||||||
|
SerializerFormat *contents);
|
||||||
|
SerializerFormat *serializer_make_record (Serializer *serializer,
|
||||||
|
const char *name,
|
||||||
|
SerializerFormat *contents1,
|
||||||
|
...);
|
||||||
|
SerializerFormat *serializer_make_integer (Serializer *serialiser,
|
||||||
|
const char *name);
|
||||||
|
SerializerFormat *serializer_make_pointer (Serializer *serialiser,
|
||||||
|
const char *name,
|
||||||
|
SerializerFormat *target_type);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* A possibly better API/naming scheme
|
/* A possibly better API/naming scheme
|
||||||
*
|
*
|
||||||
* Serializer *serializer_new (SerializerFormat *format);
|
* Serializer *serializer_new (SerializerFormat *format);
|
||||||
@ -65,6 +83,10 @@ typedef guint SType;
|
|||||||
* different versions of the format, and they want to be able to sniff the
|
* different versions of the format, and they want to be able to sniff the
|
||||||
* format + version
|
* format + version
|
||||||
*
|
*
|
||||||
|
* The version should be part of the format. There should be a
|
||||||
|
* const char *sfile_sniff (const filename);
|
||||||
|
* that will return NULL (+ error) if the file can't be parsed
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* - Describing Types - */
|
/* - Describing Types - */
|
||||||
|
|||||||
263
stackstash.c
263
stackstash.c
@ -19,22 +19,10 @@
|
|||||||
|
|
||||||
#include "stackstash.h"
|
#include "stackstash.h"
|
||||||
|
|
||||||
typedef struct StackNode StackNode;
|
|
||||||
|
|
||||||
struct StackNode
|
|
||||||
{
|
|
||||||
StackNode * parent;
|
|
||||||
gpointer address;
|
|
||||||
StackNode * siblings;
|
|
||||||
StackNode * children;
|
|
||||||
StackNode * next; /* next leaf with the same pid */
|
|
||||||
int size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StackStash
|
struct StackStash
|
||||||
{
|
{
|
||||||
StackNode *root;
|
StackNode *root;
|
||||||
GHashTable *leaves_by_process;
|
GHashTable *nodes_by_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
static StackNode *
|
static StackNode *
|
||||||
@ -45,144 +33,115 @@ stack_node_new (void)
|
|||||||
node->children = NULL;
|
node->children = NULL;
|
||||||
node->address = NULL;
|
node->address = NULL;
|
||||||
node->parent = NULL;
|
node->parent = NULL;
|
||||||
node->next = NULL;
|
|
||||||
node->size = 0;
|
node->size = 0;
|
||||||
|
node->next = NULL;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
stack_node_destroy (gpointer p)
|
|
||||||
{
|
|
||||||
StackNode *node = p;
|
|
||||||
if (node)
|
|
||||||
{
|
|
||||||
stack_node_destroy (node->siblings);
|
|
||||||
stack_node_destroy (node->children);
|
|
||||||
g_free (node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stach */
|
/* Stach */
|
||||||
StackStash *
|
StackStash *
|
||||||
stack_stash_new (void)
|
stack_stash_new (void)
|
||||||
{
|
{
|
||||||
StackStash *stash = g_new (StackStash, 1);
|
StackStash *stash = g_new (StackStash, 1);
|
||||||
|
|
||||||
stash->leaves_by_process =
|
|
||||||
g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
||||||
stash->root = NULL;
|
stash->root = NULL;
|
||||||
|
stash->nodes_by_data = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
|
||||||
return stash;
|
return stash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static StackNode *
|
void
|
||||||
stack_node_add_trace (StackNode *node,
|
decorate_node (StackStash *stash,
|
||||||
GList *bottom,
|
StackNode *node)
|
||||||
gint size,
|
|
||||||
StackNode **leaf)
|
|
||||||
{
|
{
|
||||||
StackNode *match;
|
|
||||||
StackNode *n;
|
StackNode *n;
|
||||||
|
gboolean toplevel = TRUE;
|
||||||
|
|
||||||
if (!bottom)
|
/* FIXME: we will probably want to do this lazily,
|
||||||
{
|
* and more efficiently (only walk the tree once).
|
||||||
*leaf = NULL;
|
*/
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bottom->next)
|
for (n = node->parent; n != NULL; n = n->parent)
|
||||||
{
|
{
|
||||||
/* A leaf must always be separate, so pids can
|
if (n->address == node->address)
|
||||||
* point to them
|
|
||||||
*/
|
|
||||||
match = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (match = node; match != NULL; match = match->siblings)
|
|
||||||
{
|
{
|
||||||
if (match->address == bottom->data)
|
toplevel = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match)
|
node->toplevel = toplevel;
|
||||||
{
|
|
||||||
match = stack_node_new ();
|
|
||||||
match->address = bottom->data;
|
|
||||||
match->siblings = node;
|
|
||||||
node = match;
|
|
||||||
}
|
|
||||||
|
|
||||||
match->children =
|
|
||||||
stack_node_add_trace (match->children, bottom->next, size, leaf);
|
|
||||||
|
|
||||||
for (n = match->children; n; n = n->siblings)
|
|
||||||
n->parent = match;
|
|
||||||
|
|
||||||
if (!bottom->next)
|
node->next = g_hash_table_lookup (
|
||||||
{
|
stash->nodes_by_data, node->address);
|
||||||
match->size += size;
|
g_hash_table_insert (
|
||||||
*leaf = match;
|
stash->nodes_by_data, node->address, node);
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
stack_stash_add_trace (StackStash *stash,
|
stack_stash_add_trace (StackStash *stash,
|
||||||
Process *process,
|
gulong *addrs,
|
||||||
gulong *addrs,
|
|
||||||
int n_addrs,
|
int n_addrs,
|
||||||
int size)
|
int size)
|
||||||
{
|
{
|
||||||
GList *trace;
|
StackNode **location = &(stash->root);
|
||||||
StackNode *leaf;
|
StackNode *parent = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!n_addrs)
|
if (!n_addrs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
trace = NULL;
|
for (i = n_addrs - 1; i >= 0; --i)
|
||||||
for (i = 0; i < n_addrs; ++i)
|
{
|
||||||
trace = g_list_prepend (trace, GINT_TO_POINTER (addrs[i]));
|
StackNode *match = NULL;
|
||||||
|
StackNode *n;
|
||||||
|
|
||||||
stash->root = stack_node_add_trace (stash->root, trace, size, &leaf);
|
for (n = *location; n != NULL; n = n->siblings)
|
||||||
|
{
|
||||||
|
if (n->address == (gpointer)addrs[i])
|
||||||
|
{
|
||||||
|
match = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
leaf->next = g_hash_table_lookup (
|
if (!match)
|
||||||
stash->leaves_by_process, process);
|
{
|
||||||
g_hash_table_insert (
|
match = stack_node_new ();
|
||||||
stash->leaves_by_process, process, leaf);
|
match->address = (gpointer)addrs[i];
|
||||||
|
match->siblings = *location;
|
||||||
|
match->parent = parent;
|
||||||
|
*location = match;
|
||||||
|
|
||||||
g_list_free (trace);
|
decorate_node (stash, match);
|
||||||
|
}
|
||||||
|
|
||||||
|
location = &(match->children);
|
||||||
|
parent = match;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->size += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct CallbackInfo
|
|
||||||
{
|
|
||||||
StackFunction func;
|
|
||||||
gpointer data;
|
|
||||||
} CallbackInfo;
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
do_callback (gpointer key, gpointer value, gpointer data)
|
do_callback (StackNode *node,
|
||||||
|
GSList *trace,
|
||||||
|
StackFunction stack_func,
|
||||||
|
gpointer data)
|
||||||
{
|
{
|
||||||
CallbackInfo *info = data;
|
GSList link;
|
||||||
Process *process = key;
|
|
||||||
StackNode *n;
|
if (!node)
|
||||||
StackNode *leaf = value;
|
return;
|
||||||
while (leaf)
|
|
||||||
{
|
|
||||||
GSList *trace;
|
|
||||||
|
|
||||||
trace = NULL;
|
link.next = trace;
|
||||||
for (n = leaf; n; n = n->parent)
|
link.data = node->address;
|
||||||
trace = g_slist_prepend (trace, n->address);
|
|
||||||
|
do_callback (node->siblings, trace, stack_func, data);
|
||||||
|
do_callback (node->children, &link, stack_func, data);
|
||||||
|
|
||||||
info->func (process, trace, leaf->size, info->data);
|
if (node->size)
|
||||||
|
stack_func (&link, node->size, data);
|
||||||
g_slist_free (trace);
|
|
||||||
|
|
||||||
leaf = leaf->next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -190,11 +149,7 @@ stack_stash_foreach (StackStash *stash,
|
|||||||
StackFunction stack_func,
|
StackFunction stack_func,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
CallbackInfo info;
|
do_callback (stash->root, NULL, stack_func, data);
|
||||||
info.func = stack_func;
|
|
||||||
info.data = data;
|
|
||||||
|
|
||||||
g_hash_table_foreach (stash->leaves_by_process, do_callback, &info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -213,6 +168,90 @@ void
|
|||||||
stack_stash_free (StackStash *stash)
|
stack_stash_free (StackStash *stash)
|
||||||
{
|
{
|
||||||
stack_node_free (stash->root);
|
stack_node_free (stash->root);
|
||||||
g_hash_table_destroy (stash->leaves_by_process);
|
g_hash_table_destroy (stash->nodes_by_data);
|
||||||
|
|
||||||
g_free (stash);
|
g_free (stash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StackNode *
|
||||||
|
stack_stash_find_node (StackStash *stash,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (stash != NULL, NULL);
|
||||||
|
|
||||||
|
return g_hash_table_lookup (stash->nodes_by_data, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
stack_node_list_leaves (StackNode *node,
|
||||||
|
GList **leaves)
|
||||||
|
{
|
||||||
|
StackNode *n;
|
||||||
|
|
||||||
|
if (node->size > 0)
|
||||||
|
*leaves = g_list_prepend (*leaves, node);
|
||||||
|
|
||||||
|
for (n = node->children; n != NULL; n = n->siblings)
|
||||||
|
stack_node_list_leaves (n, leaves);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
StackNodeFunc func;
|
||||||
|
gpointer data;
|
||||||
|
} Info;
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_foreach (gpointer key, gpointer value, gpointer data)
|
||||||
|
{
|
||||||
|
Info *info = data;
|
||||||
|
|
||||||
|
info->func (value, info->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
stack_stash_foreach_by_address (StackStash *stash,
|
||||||
|
StackNodeFunc func,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
Info info;
|
||||||
|
info.func = func;
|
||||||
|
info.data = data;
|
||||||
|
|
||||||
|
g_hash_table_foreach (stash->nodes_by_data, do_foreach, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
StackNode *
|
||||||
|
stack_stash_get_root (StackStash *stash)
|
||||||
|
{
|
||||||
|
return stash->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
build_hash_table (StackNode *node,
|
||||||
|
StackStash *stash)
|
||||||
|
{
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
build_hash_table (node->siblings, stash);
|
||||||
|
build_hash_table (node->children, stash);
|
||||||
|
|
||||||
|
node->next = g_hash_table_lookup (
|
||||||
|
stash->nodes_by_data, node->address);
|
||||||
|
g_hash_table_insert (
|
||||||
|
stash->nodes_by_data, node->address, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
StackStash *
|
||||||
|
stack_stash_new_from_root (StackNode *root)
|
||||||
|
{
|
||||||
|
StackStash *stash = g_new (StackStash, 1);
|
||||||
|
|
||||||
|
stash->root = root;
|
||||||
|
stash->nodes_by_data = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
|
||||||
|
build_hash_table (stash->root, stash);
|
||||||
|
|
||||||
|
return stash;
|
||||||
|
}
|
||||||
|
|||||||
33
stackstash.h
33
stackstash.h
@ -21,25 +21,50 @@
|
|||||||
#define STACK_STASH_H
|
#define STACK_STASH_H
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "process.h"
|
|
||||||
|
|
||||||
typedef struct StackStash StackStash;
|
typedef struct StackStash StackStash;
|
||||||
|
|
||||||
typedef void (* StackFunction) (Process *process,
|
typedef struct StackNode StackNode;
|
||||||
GSList *trace,
|
|
||||||
|
struct StackNode
|
||||||
|
{
|
||||||
|
gpointer address;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
StackNode * parent;
|
||||||
|
StackNode * siblings;
|
||||||
|
StackNode * children;
|
||||||
|
|
||||||
|
StackNode * next;
|
||||||
|
|
||||||
|
gboolean toplevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (* StackFunction) (GSList *trace,
|
||||||
gint size,
|
gint size,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
|
||||||
/* Stach */
|
/* Stach */
|
||||||
StackStash *stack_stash_new (void);
|
StackStash *stack_stash_new (void);
|
||||||
void stack_stash_add_trace (StackStash *stash,
|
void stack_stash_add_trace (StackStash *stash,
|
||||||
Process *process,
|
|
||||||
gulong *addrs,
|
gulong *addrs,
|
||||||
gint n_addrs,
|
gint n_addrs,
|
||||||
int size);
|
int size);
|
||||||
void stack_stash_foreach (StackStash *stash,
|
void stack_stash_foreach (StackStash *stash,
|
||||||
StackFunction stack_func,
|
StackFunction stack_func,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
StackNode *stack_stash_find_node (StackStash *stash,
|
||||||
|
gpointer address);
|
||||||
|
/* FIXME: should probably return a list */
|
||||||
|
void stack_node_list_leaves (StackNode *node,
|
||||||
|
GList **leaves);
|
||||||
|
typedef void (* StackNodeFunc) (StackNode *node,
|
||||||
|
gpointer data);
|
||||||
|
void stack_stash_foreach_by_address (StackStash *stash,
|
||||||
|
StackNodeFunc func,
|
||||||
|
gpointer data);
|
||||||
void stack_stash_free (StackStash *stash);
|
void stack_stash_free (StackStash *stash);
|
||||||
|
StackNode *stack_stash_get_root (StackStash *stash);
|
||||||
|
StackStash *stack_stash_new_from_root (StackNode *root);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -32,63 +32,21 @@
|
|||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "watch.h"
|
#include "watch.h"
|
||||||
#include "signal-handler.h"
|
#include "signal-handler.h"
|
||||||
|
#include "collector.h"
|
||||||
|
|
||||||
typedef struct Application Application;
|
typedef struct Application Application;
|
||||||
struct Application
|
struct Application
|
||||||
{
|
{
|
||||||
int fd;
|
Collector * collector;
|
||||||
StackStash *stack_stash;
|
|
||||||
char * outfile;
|
char * outfile;
|
||||||
GMainLoop * main_loop;
|
GMainLoop * main_loop;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
read_trace (StackStash *stash,
|
|
||||||
SysprofStackTrace *trace,
|
|
||||||
GTimeVal now)
|
|
||||||
{
|
|
||||||
Process *process;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
process = process_get_from_pid (trace->pid);
|
|
||||||
|
|
||||||
for (i = 0; i < trace->n_addresses; ++i)
|
|
||||||
{
|
|
||||||
process_ensure_map (process, trace->pid,
|
|
||||||
(gulong)trace->addresses[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
stack_stash_add_trace (
|
|
||||||
stash, process,
|
|
||||||
(gulong *)trace->addresses, trace->n_addresses, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
on_read (gpointer data)
|
|
||||||
{
|
|
||||||
Application *app = data;
|
|
||||||
SysprofStackTrace trace;
|
|
||||||
int bytesread;
|
|
||||||
GTimeVal now;
|
|
||||||
|
|
||||||
bytesread = read (app->fd, &trace, sizeof (trace));
|
|
||||||
g_get_current_time (&now);
|
|
||||||
|
|
||||||
if (bytesread < 0)
|
|
||||||
{
|
|
||||||
perror("read");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesread > 0)
|
|
||||||
read_trace (app->stack_stash, &trace, now);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
dump_data (Application *app)
|
dump_data (Application *app)
|
||||||
{
|
{
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
Profile *profile = profile_new (app->stack_stash);
|
Profile *profile = collector_create_profile (app->collector);
|
||||||
|
|
||||||
profile_save (profile, app->outfile, &err);
|
profile_save (profile, app->outfile, &err);
|
||||||
|
|
||||||
@ -104,10 +62,11 @@ signal_handler (int signo, gpointer data)
|
|||||||
{
|
{
|
||||||
Application *app = data;
|
Application *app = data;
|
||||||
|
|
||||||
g_print ("signal %d caught: dumping data\n", signo);
|
|
||||||
|
|
||||||
dump_data (app);
|
dump_data (app);
|
||||||
|
|
||||||
|
while (g_main_iteration (FALSE))
|
||||||
|
;
|
||||||
|
|
||||||
g_main_loop_quit (app->main_loop);
|
g_main_loop_quit (app->main_loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,16 +126,17 @@ main (int argc,
|
|||||||
if (quit)
|
if (quit)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
app->fd = fd;
|
app->collector = collector_new (NULL, NULL);
|
||||||
app->outfile = g_strdup (argv[1]);
|
app->outfile = g_strdup (argv[1]);
|
||||||
app->stack_stash = stack_stash_new ();
|
|
||||||
app->main_loop = g_main_loop_new (NULL, 0);
|
app->main_loop = g_main_loop_new (NULL, 0);
|
||||||
|
|
||||||
|
/* FIXME: check the errors */
|
||||||
signal_set_handler (SIGTERM, signal_handler, app, NULL);
|
signal_set_handler (SIGTERM, signal_handler, app, NULL);
|
||||||
signal_set_handler (SIGINT, signal_handler, app, NULL);
|
signal_set_handler (SIGINT, signal_handler, app, NULL);
|
||||||
|
|
||||||
|
/* FIXME: check the error */
|
||||||
|
collector_start (app->collector, NULL);
|
||||||
|
|
||||||
fd_add_watch (app->fd, app);
|
|
||||||
fd_set_read_callback (app->fd, on_read);
|
|
||||||
g_main_loop_run (app->main_loop);
|
g_main_loop_run (app->main_loop);
|
||||||
|
|
||||||
signal_unset_handler (SIGTERM);
|
signal_unset_handler (SIGTERM);
|
||||||
|
|||||||
297
sysprof.c
297
sysprof.c
@ -1,4 +1,4 @@
|
|||||||
/* Sysprof -- Sampling, systemwide CPU profiler
|
/* Sysprof -- Sampling, systemwide CPU profiler
|
||||||
* Copyright 2004, Red Hat, Inc.
|
* Copyright 2004, Red Hat, Inc.
|
||||||
* Copyright 2004, 2005, Soeren Sandmann
|
* Copyright 2004, 2005, Soeren Sandmann
|
||||||
*
|
*
|
||||||
@ -19,23 +19,14 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <glade/glade.h>
|
#include <glade/glade.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "binfile.h"
|
|
||||||
#include "watch.h"
|
|
||||||
#include "module/sysprof-module.h"
|
|
||||||
#include "stackstash.h"
|
|
||||||
#include "profile.h"
|
|
||||||
#include "treeviewutils.h"
|
#include "treeviewutils.h"
|
||||||
|
#include "profile.h"
|
||||||
|
#include "collector.h"
|
||||||
|
|
||||||
/* FIXME - not10 */
|
/* FIXME - not10 */
|
||||||
#define _(a) a
|
#define _(a) a
|
||||||
@ -53,9 +44,9 @@ typedef enum
|
|||||||
|
|
||||||
struct Application
|
struct Application
|
||||||
{
|
{
|
||||||
int input_fd;
|
Collector * collector;
|
||||||
|
|
||||||
State state;
|
State state;
|
||||||
StackStash * stash;
|
|
||||||
|
|
||||||
GtkWidget * main_window;
|
GtkWidget * main_window;
|
||||||
GdkPixbuf * icon;
|
GdkPixbuf * icon;
|
||||||
@ -82,8 +73,6 @@ struct Application
|
|||||||
ProfileDescendant * descendants;
|
ProfileDescendant * descendants;
|
||||||
ProfileCaller * callers;
|
ProfileCaller * callers;
|
||||||
|
|
||||||
int n_samples;
|
|
||||||
|
|
||||||
int timeout_id;
|
int timeout_id;
|
||||||
int generating_profile;
|
int generating_profile;
|
||||||
|
|
||||||
@ -104,7 +93,6 @@ struct Application
|
|||||||
*
|
*
|
||||||
* Model/View/Controller is a possibility.
|
* Model/View/Controller is a possibility.
|
||||||
*/
|
*/
|
||||||
GTimeVal latest_reset;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -112,16 +100,20 @@ show_samples_timeout (gpointer data)
|
|||||||
{
|
{
|
||||||
Application *app = data;
|
Application *app = data;
|
||||||
char *label;
|
char *label;
|
||||||
|
int n_samples;
|
||||||
|
|
||||||
switch (app->state)
|
switch (app->state)
|
||||||
{
|
{
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
label = g_strdup ("Samples: 0");
|
n_samples = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PROFILING:
|
case PROFILING:
|
||||||
|
n_samples = collector_get_n_samples (app->collector);
|
||||||
|
break;
|
||||||
|
|
||||||
case DISPLAYING:
|
case DISPLAYING:
|
||||||
label = g_strdup_printf ("Samples: %d", app->n_samples);
|
n_samples = profile_get_size (app->profile);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -129,6 +121,8 @@ show_samples_timeout (gpointer data)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label = g_strdup_printf ("Samples: %d", n_samples);
|
||||||
|
|
||||||
gtk_label_set_label (GTK_LABEL (app->samples_label), label);
|
gtk_label_set_label (GTK_LABEL (app->samples_label), label);
|
||||||
|
|
||||||
g_free (label);
|
g_free (label);
|
||||||
@ -156,7 +150,9 @@ update_sensitivity (Application *app)
|
|||||||
gboolean sensitive_reset_button;
|
gboolean sensitive_reset_button;
|
||||||
|
|
||||||
GtkWidget *active_radio_button;
|
GtkWidget *active_radio_button;
|
||||||
|
|
||||||
|
gboolean has_samples;
|
||||||
|
|
||||||
switch (app->state)
|
switch (app->state)
|
||||||
{
|
{
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
@ -170,9 +166,11 @@ update_sensitivity (Application *app)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PROFILING:
|
case PROFILING:
|
||||||
sensitive_profile_button = (app->n_samples > 0);
|
has_samples = (collector_get_n_samples (app->collector) > 0);
|
||||||
sensitive_save_as_button = (app->n_samples > 0);
|
|
||||||
sensitive_reset_button = (app->n_samples > 0);
|
sensitive_profile_button = has_samples;
|
||||||
|
sensitive_save_as_button = has_samples;
|
||||||
|
sensitive_reset_button = has_samples;
|
||||||
sensitive_start_button = TRUE;
|
sensitive_start_button = TRUE;
|
||||||
sensitive_tree_views = FALSE;
|
sensitive_tree_views = FALSE;
|
||||||
sensitive_samples_label = TRUE;
|
sensitive_samples_label = TRUE;
|
||||||
@ -250,82 +248,6 @@ set_busy (GtkWidget *widget, gboolean busy)
|
|||||||
gdk_flush ();
|
gdk_flush ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
|
||||||
timeval_to_ms (const GTimeVal *timeval)
|
|
||||||
{
|
|
||||||
return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double
|
|
||||||
time_diff (const GTimeVal *first,
|
|
||||||
const GTimeVal *second)
|
|
||||||
{
|
|
||||||
double first_ms = timeval_to_ms (first);
|
|
||||||
double second_ms = timeval_to_ms (second);
|
|
||||||
|
|
||||||
return first_ms - second_ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RESET_DEAD_PERIOD 25
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_read (gpointer data)
|
|
||||||
{
|
|
||||||
Application *app = data;
|
|
||||||
SysprofStackTrace trace;
|
|
||||||
GTimeVal now;
|
|
||||||
int rd;
|
|
||||||
|
|
||||||
rd = read (app->input_fd, &trace, sizeof (trace));
|
|
||||||
|
|
||||||
if (app->state != PROFILING)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rd == -1 && errno == EWOULDBLOCK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_get_current_time (&now);
|
|
||||||
|
|
||||||
/* After a reset we ignore samples for a short period so that
|
|
||||||
* a reset will actually cause 'samples' to become 0
|
|
||||||
*/
|
|
||||||
if (time_diff (&now, &app->latest_reset) < RESET_DEAD_PERIOD)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
int i;
|
|
||||||
g_print ("pid: %d\n", trace.pid);
|
|
||||||
for (i=0; i < trace.n_addresses; ++i)
|
|
||||||
g_print ("rd: %08x\n", trace.addresses[i]);
|
|
||||||
g_print ("-=-\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (rd > 0 && !app->generating_profile && trace.n_addresses)
|
|
||||||
{
|
|
||||||
Process *process = process_get_from_pid (trace.pid);
|
|
||||||
int i;
|
|
||||||
/* char *filename = NULL; */
|
|
||||||
|
|
||||||
/* if (*trace.filename) */
|
|
||||||
/* filename = trace.filename; */
|
|
||||||
|
|
||||||
for (i = 0; i < trace.n_addresses; ++i)
|
|
||||||
{
|
|
||||||
process_ensure_map (process, trace.pid,
|
|
||||||
(gulong)trace.addresses[i]);
|
|
||||||
}
|
|
||||||
g_assert (!app->generating_profile);
|
|
||||||
|
|
||||||
stack_stash_add_trace (
|
|
||||||
app->stash, process,
|
|
||||||
(gulong *)trace.addresses, trace.n_addresses, 1);
|
|
||||||
|
|
||||||
app->n_samples++;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_sensitivity (app);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_application_title (Application *app,
|
set_application_title (Application *app,
|
||||||
const char * name)
|
const char * name)
|
||||||
@ -349,7 +271,7 @@ set_application_title (Application *app,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
gtk_window_set_title (GTK_WINDOW (app->main_window),
|
gtk_window_set_title (GTK_WINDOW (app->main_window),
|
||||||
"System Profiler");
|
"System Collector");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,46 +287,13 @@ delete_data (Application *app)
|
|||||||
gtk_tree_view_set_model (GTK_TREE_VIEW (app->callers_view), NULL);
|
gtk_tree_view_set_model (GTK_TREE_VIEW (app->callers_view), NULL);
|
||||||
gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL);
|
gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collector_reset (app->collector);
|
||||||
|
|
||||||
if (app->stash)
|
|
||||||
stack_stash_free (app->stash);
|
|
||||||
app->stash = stack_stash_new ();
|
|
||||||
process_flush_caches ();
|
|
||||||
app->n_samples = 0;
|
|
||||||
queue_show_samples (app);
|
queue_show_samples (app);
|
||||||
|
|
||||||
app->profile_from_file = FALSE;
|
app->profile_from_file = FALSE;
|
||||||
set_application_title (app, NULL);
|
set_application_title (app, NULL);
|
||||||
g_get_current_time (&app->latest_reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
empty_file_descriptor (Application *app)
|
|
||||||
{
|
|
||||||
int rd;
|
|
||||||
SysprofStackTrace trace;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
rd = read (app->input_fd, &trace, sizeof (trace));
|
|
||||||
|
|
||||||
} while (rd != -1); /* until EWOULDBLOCK */
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
start_profiling (gpointer data)
|
|
||||||
{
|
|
||||||
Application *app = data;
|
|
||||||
|
|
||||||
app->state = PROFILING;
|
|
||||||
|
|
||||||
update_sensitivity (app);
|
|
||||||
|
|
||||||
/* Make sure samples generated between 'start clicked' and now
|
|
||||||
* are deleted
|
|
||||||
*/
|
|
||||||
empty_file_descriptor (app);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -432,27 +321,6 @@ sorry (GtkWidget *parent_window,
|
|||||||
gtk_widget_destroy (dialog);
|
gtk_widget_destroy (dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
load_module (void)
|
|
||||||
{
|
|
||||||
int exit_status = -1;
|
|
||||||
char *dummy1, *dummy2;
|
|
||||||
|
|
||||||
if (g_spawn_command_line_sync ("/sbin/modprobe sysprof-module",
|
|
||||||
&dummy1, &dummy2,
|
|
||||||
&exit_status,
|
|
||||||
NULL))
|
|
||||||
{
|
|
||||||
if (WIFEXITED (exit_status))
|
|
||||||
exit_status = WEXITSTATUS (exit_status);
|
|
||||||
|
|
||||||
g_free (dummy1);
|
|
||||||
g_free (dummy2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (exit_status == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button)
|
on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button)
|
||||||
{
|
{
|
||||||
@ -471,41 +339,25 @@ on_start_toggled (GtkWidget *widget, gpointer data)
|
|||||||
GTK_TOGGLE_TOOL_BUTTON (app->start_button)))
|
GTK_TOGGLE_TOOL_BUTTON (app->start_button)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (app->input_fd == -1)
|
delete_data (app);
|
||||||
|
|
||||||
|
/* FIXME: get the real error message */
|
||||||
|
if (!collector_start (app->collector, NULL))
|
||||||
{
|
{
|
||||||
int fd;
|
sorry (app->main_window,
|
||||||
|
"Can't open /proc/sysprof-trace. You need to insert\n"
|
||||||
|
"the sysprof kernel module. Run\n"
|
||||||
|
"\n"
|
||||||
|
" modprobe sysprof-module\n"
|
||||||
|
"\n"
|
||||||
|
"as root.");
|
||||||
|
|
||||||
fd = open ("/proc/sysprof-trace", O_RDONLY);
|
return;
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
load_module();
|
|
||||||
|
|
||||||
fd = open ("/proc/sysprof-trace", O_RDONLY);
|
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
sorry (app->main_window,
|
|
||||||
"Can't open /proc/sysprof-trace. You need to insert\n"
|
|
||||||
"the sysprof kernel module. Run\n"
|
|
||||||
"\n"
|
|
||||||
" modprobe sysprof-module\n"
|
|
||||||
"\n"
|
|
||||||
"as root.");
|
|
||||||
|
|
||||||
update_sensitivity (app);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app->input_fd = fd;
|
|
||||||
fd_add_watch (app->input_fd, app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fd_set_read_callback (app->input_fd, on_read);
|
app->state = PROFILING;
|
||||||
|
|
||||||
delete_data (app);
|
update_sensitivity (app);
|
||||||
|
|
||||||
g_idle_add_full (G_PRIORITY_LOW, start_profiling, app, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@ -533,13 +385,13 @@ enum
|
|||||||
DESCENDANTS_OBJECT
|
DESCENDANTS_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
static ProfileObject *
|
static char *
|
||||||
get_current_object (Application *app)
|
get_current_object (Application *app)
|
||||||
{
|
{
|
||||||
GtkTreeSelection *selection;
|
GtkTreeSelection *selection;
|
||||||
GtkTreeModel *model;
|
GtkTreeModel *model;
|
||||||
GtkTreeIter selected;
|
GtkTreeIter selected;
|
||||||
ProfileObject *object;
|
char *object;
|
||||||
|
|
||||||
selection = gtk_tree_view_get_selection (app->object_view);
|
selection = gtk_tree_view_get_selection (app->object_view);
|
||||||
|
|
||||||
@ -580,14 +432,14 @@ fill_main_list (Application *app)
|
|||||||
ProfileObject *object = list->data;
|
ProfileObject *object = list->data;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
double profile_size = profile_get_size (profile);
|
double profile_size = profile_get_size (profile);
|
||||||
|
|
||||||
gtk_list_store_append (list_store, &iter);
|
gtk_list_store_append (list_store, &iter);
|
||||||
|
|
||||||
gtk_list_store_set (list_store, &iter,
|
gtk_list_store_set (list_store, &iter,
|
||||||
OBJECT_NAME, object->name,
|
OBJECT_NAME, object->name,
|
||||||
OBJECT_SELF, 100.0 * object->self / profile_size,
|
OBJECT_SELF, 100.0 * object->self / profile_size,
|
||||||
OBJECT_TOTAL, 100.0 * object->total / profile_size,
|
OBJECT_TOTAL, 100.0 * object->total / profile_size,
|
||||||
OBJECT_OBJECT, object,
|
OBJECT_OBJECT, object->name,
|
||||||
-1);
|
-1);
|
||||||
}
|
}
|
||||||
g_list_free (objects);
|
g_list_free (objects);
|
||||||
@ -627,11 +479,11 @@ add_node (GtkTreeStore *store,
|
|||||||
gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
|
gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
|
||||||
|
|
||||||
gtk_tree_store_set (store, &iter,
|
gtk_tree_store_set (store, &iter,
|
||||||
DESCENDANTS_NAME, node->object->name,
|
DESCENDANTS_NAME, node->name,
|
||||||
DESCENDANTS_SELF, 100 * (node->self)/(double)size,
|
DESCENDANTS_SELF, 100 * (node->self)/(double)size,
|
||||||
DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
|
DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
|
||||||
DESCENDANTS_TOTAL, 100 * (node->total)/(double)size,
|
DESCENDANTS_TOTAL, 100 * (node->total)/(double)size,
|
||||||
DESCENDANTS_OBJECT, node->object,
|
DESCENDANTS_OBJECT, node->name,
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
add_node (store, size, parent, node->siblings);
|
add_node (store, size, parent, node->siblings);
|
||||||
@ -662,7 +514,7 @@ fill_descendants_tree (Application *app)
|
|||||||
|
|
||||||
if (app->profile)
|
if (app->profile)
|
||||||
{
|
{
|
||||||
ProfileObject *object = get_current_object (app);
|
char *object = get_current_object (app);
|
||||||
if (object)
|
if (object)
|
||||||
{
|
{
|
||||||
app->descendants =
|
app->descendants =
|
||||||
@ -702,8 +554,8 @@ add_callers (GtkListStore *list_store,
|
|||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
double profile_size = profile_get_size (profile);
|
double profile_size = profile_get_size (profile);
|
||||||
|
|
||||||
if (callers->object)
|
if (callers->name)
|
||||||
name = callers->object->name;
|
name = callers->name;
|
||||||
else
|
else
|
||||||
name = "<spontaneous>";
|
name = "<spontaneous>";
|
||||||
|
|
||||||
@ -713,7 +565,7 @@ add_callers (GtkListStore *list_store,
|
|||||||
CALLERS_NAME, name,
|
CALLERS_NAME, name,
|
||||||
CALLERS_SELF, 100.0 * callers->self / profile_size,
|
CALLERS_SELF, 100.0 * callers->self / profile_size,
|
||||||
CALLERS_TOTAL, 100.0 * callers->total / profile_size,
|
CALLERS_TOTAL, 100.0 * callers->total / profile_size,
|
||||||
CALLERS_OBJECT, callers->object,
|
CALLERS_OBJECT, callers->name,
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
callers = callers->next;
|
callers = callers->next;
|
||||||
@ -743,7 +595,7 @@ fill_callers_list (Application *app)
|
|||||||
|
|
||||||
if (app->profile)
|
if (app->profile)
|
||||||
{
|
{
|
||||||
ProfileObject *object = get_current_object (app);
|
char *object = get_current_object (app);
|
||||||
if (object)
|
if (object)
|
||||||
{
|
{
|
||||||
app->callers = profile_list_callers (app->profile, object);
|
app->callers = profile_list_callers (app->profile, object);
|
||||||
@ -784,7 +636,7 @@ ensure_profile (Application *app)
|
|||||||
if (app->profile)
|
if (app->profile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
app->profile = profile_new (app->stash);
|
app->profile = collector_create_profile (app->collector);
|
||||||
|
|
||||||
fill_lists (app);
|
fill_lists (app);
|
||||||
|
|
||||||
@ -956,8 +808,6 @@ set_loaded_profile (Application *app,
|
|||||||
|
|
||||||
app->state = DISPLAYING;
|
app->state = DISPLAYING;
|
||||||
|
|
||||||
app->n_samples = profile_get_size (profile);
|
|
||||||
|
|
||||||
app->profile = profile;
|
app->profile = profile;
|
||||||
app->profile_from_file = TRUE;
|
app->profile_from_file = TRUE;
|
||||||
|
|
||||||
@ -1041,8 +891,14 @@ on_open_clicked (gpointer widget,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_delete (GtkWidget *window)
|
on_delete (GtkWidget *window,
|
||||||
|
Application *app)
|
||||||
{
|
{
|
||||||
|
/* Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=317775
|
||||||
|
*/
|
||||||
|
while (gtk_main_iteration ())
|
||||||
|
;
|
||||||
|
|
||||||
gtk_main_quit ();
|
gtk_main_quit ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1123,6 +979,7 @@ expand_descendants_tree (Application *app)
|
|||||||
path = gtk_tree_path_copy (path);
|
path = gtk_tree_path_copy (path);
|
||||||
gtk_tree_path_next (path);
|
gtk_tree_path_next (path);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_tree_path_free (path);
|
gtk_tree_path_free (path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1158,7 +1015,7 @@ on_object_selection_changed (GtkTreeSelection *selection,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
really_goto_object (Application *app,
|
really_goto_object (Application *app,
|
||||||
ProfileObject *object)
|
char *object)
|
||||||
{
|
{
|
||||||
GtkTreeModel *profile_objects;
|
GtkTreeModel *profile_objects;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
@ -1170,13 +1027,13 @@ really_goto_object (Application *app,
|
|||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
ProfileObject *profile_object;
|
char *list_object;
|
||||||
|
|
||||||
gtk_tree_model_get (profile_objects, &iter,
|
gtk_tree_model_get (profile_objects, &iter,
|
||||||
OBJECT_OBJECT, &profile_object,
|
OBJECT_OBJECT, &list_object,
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
if (profile_object == object)
|
if (list_object == object)
|
||||||
{
|
{
|
||||||
found = TRUE;
|
found = TRUE;
|
||||||
break;
|
break;
|
||||||
@ -1202,7 +1059,7 @@ goto_object (Application *app,
|
|||||||
{
|
{
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
|
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
|
||||||
ProfileObject *object;
|
char *object;
|
||||||
|
|
||||||
if (!gtk_tree_model_get_iter (model, &iter, path))
|
if (!gtk_tree_model_get_iter (model, &iter, path))
|
||||||
return;
|
return;
|
||||||
@ -1213,7 +1070,6 @@ goto_object (Application *app,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
really_goto_object (app, object);
|
really_goto_object (app, object);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1424,16 +1280,22 @@ build_gui (Application *app)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_new_sample (gpointer data)
|
||||||
|
{
|
||||||
|
Application *app = data;
|
||||||
|
|
||||||
|
if (app->state == PROFILING)
|
||||||
|
update_sensitivity (app);
|
||||||
|
}
|
||||||
|
|
||||||
static Application *
|
static Application *
|
||||||
application_new (void)
|
application_new (void)
|
||||||
{
|
{
|
||||||
Application *app = g_new0 (Application, 1);
|
Application *app = g_new0 (Application, 1);
|
||||||
|
|
||||||
app->stash = stack_stash_new ();
|
|
||||||
app->input_fd = -1;
|
|
||||||
app->state = INITIAL;
|
|
||||||
|
|
||||||
g_get_current_time (&app->latest_reset);
|
app->collector = collector_new (on_new_sample, app);
|
||||||
|
app->state = INITIAL;
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@ -1480,12 +1342,7 @@ main (int argc, char **argv)
|
|||||||
gtk_init (&argc, &argv);
|
gtk_init (&argc, &argv);
|
||||||
|
|
||||||
app = application_new ();
|
app = application_new ();
|
||||||
|
|
||||||
#if 0
|
|
||||||
nice (-19);
|
|
||||||
g_timeout_add (10, on_timeout, app);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!build_gui (app))
|
if (!build_gui (app))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user