summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPauli Nieminen <suokkos@gmail.com>2011-06-02 08:38:58 +0300
committerPauli Nieminen <suokkos@gmail.com>2011-06-02 08:38:58 +0300
commit8d7198099d4984043cc5c7ad60f240c6f17b1653 (patch)
tree64c6c4bb6347ce12da048ad0d1e474732a087bcb
parentdfac47cf3aa4279c25915a1a8f892722d1ffe4e5 (diff)
Imported Upstream version 1.1.6upstream/1.1.6upstream
-rw-r--r--60-sysprof.rules2
-rw-r--r--ChangeLog708
-rw-r--r--Makefile.am163
-rw-r--r--Makefile.in1334
-rw-r--r--NEWS7
-rw-r--r--README44
-rw-r--r--TODO1531
-rw-r--r--binfile.c813
-rw-r--r--binfile.h32
-rw-r--r--collector.c804
-rw-r--r--collector.h (renamed from module/sysprof-module.h)34
-rw-r--r--config.h.in3
-rwxr-xr-xconfigure407
-rw-r--r--configure.ac78
-rw-r--r--demangle.c9595
-rw-r--r--elfparser.c813
-rw-r--r--elfparser.h57
-rw-r--r--footreedatalist.c577
-rw-r--r--footreedatalist.h82
-rw-r--r--footreestore.c3355
-rw-r--r--footreestore.h158
-rw-r--r--module/Makefile71
-rw-r--r--module/sysprof-module.c282
-rw-r--r--perf_counter.h805
-rw-r--r--process.c444
-rw-r--r--process.h61
-rw-r--r--profile.c937
-rw-r--r--profile.h26
-rw-r--r--sfile.c1532
-rw-r--r--sfile.h58
-rw-r--r--sformat.c646
-rw-r--r--sformat.h67
-rw-r--r--signal-handler.c215
-rw-r--r--signal-handler.h27
-rw-r--r--stackstash.c427
-rw-r--r--stackstash.h71
-rw-r--r--sysprof-cli.c151
-rw-r--r--sysprof-icon-16.pngbin0 -> 847 bytes
-rw-r--r--sysprof-icon-24.pngbin0 -> 1467 bytes
-rw-r--r--sysprof-icon-32.pngbin0 -> 2305 bytes
-rw-r--r--sysprof-icon-48.pngbin0 -> 3915 bytes
-rw-r--r--sysprof-icon.pngbin3761 -> 0 bytes
-rw-r--r--sysprof.c1483
-rw-r--r--sysprof.glade497
-rw-r--r--testelf.c70
-rw-r--r--testunwind.c27
-rw-r--r--tracker.c1129
-rw-r--r--tracker.h29
-rw-r--r--treeviewutils.c430
-rw-r--r--treeviewutils.h32
-rw-r--r--unwind.c399
-rw-r--r--unwind.h3
-rw-r--r--util.h40
53 files changed, 24845 insertions, 5711 deletions
diff --git a/60-sysprof.rules b/60-sysprof.rules
new file mode 100644
index 0000000..0cbf400
--- /dev/null
+++ b/60-sysprof.rules
@@ -0,0 +1,2 @@
+KERNEL=="sysprof-trace", MODE="0644"
+
diff --git a/ChangeLog b/ChangeLog
index 4dcf48f..e69de29 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,708 +0,0 @@
-2008-04-27 Soren Sandmann <sandmann@daimi.au.dk>
-
- * -=-=-=- Release sysprof 1.0.10 -=-=-=-
-
- * configure.ac: Bump version
-
- * sysprof.c: Bump year
-
-2007-10-21 Soren Sandmann <sandmann@daimi.au.dk>
-
- * -=-=-=- Release sysprof 1.0.9 -=-=-=-
-
- * configure.ac: Bump version.
-
- * Makefile.am: Add explicit /sbin in insert-module
-
-2007-10-21 Soren Sandmann <sandmann@daimi.au.dk>
-
- * module/Makefile: Delete CFLAGS reminiscence of 2.4 support.
-
-2006-12-09 Soren Sandmann <sandmann@daimi.au.dk>
-
- * =-=-=-= Release sysprof 1.0.8 =-=-=-=
-
- * module/sysprof-module.c (timer_notify): Add forgotten
- put_cpu_var().
-
-2006-11-16 Soren Sandmann <sandmann@daimi.au.dk>
-
- * =-=-=-= Release 1.0.7 =-=-=-=
-
- * process.c (process_lookup_symbol): Also treat 0x01 as "In
- kernel", even when there is a map for the 0x00 address. (Tim
- Rowley).
-
- * module/sysprof-module.c: Check for profiling enabled after
- potentially including config.h
-
-2006-11-02 Soren Sandmann <sandmann@daimi.au.dk>
-
- * -=-=-=- Release sysprof 1.0.6 -=-=-=-
-
- * module/sysprof-module.c: Include config.h on kernels < 2.6.18.
-
-2006-10-29 Soren Sandmann <sandmann@daimi.au.dk>
-
- * -=-=-=- Release sysprof 1.0.5 -=-=-=-
-
- * binfile.c (read_symbols): Store symbols in offsets into original
- file, not debug file. Fixes symbols when the debug binaries have
- different text offsets than the real binaries.
-
-2006-10-29 Soren Sandmann <sandmann@daimi.au.dk>
-
- * =-=-=-= Release sysprof 1.0.4 =-=-=-=
-
- * module/sysprof-module.c: Remove include of linux/config.h
-
-Sat Aug 12 16:27:13 2006 Søren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (timer_notify): Bugfix for SMP kernel:
- Make n_samples a per-CPU variable. Protect timer_notify() with an
- atomic variable.
-
-2006-06-08 Soren Sandmann <sandmann@redhat.com>
-
- * announce-1.0.3: Fix forgotten 1.0.2 -> 1.0.3.
-
-2006-06-08 Soren Sandmann <sandmann@redhat.com>
-
- * =-=-=-= Release sysprof 1.0.3 =-=-=-=
-
- * configure.ac: Bump version number
-
- * announce-1.0.3: Add this file.
-
- * sysprof.c (RESET_DEAD_PERIOD): Increase this to 250
-
-2006-06-05 Soren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (init_module): Copy the proc_fops and
- update the poll entry in the copy, rather than writing directly to
- the proc_fops. Fixes compilation on the latest kernels.
-
-Thu Feb 23 21:37:21 2006 Soeren Sandmann <sandmann@redhat.com>
-
- * =-=-=-= Release sysprof 1.0.2 =-=-=-=
-
- * configure.ac: Bump version number
-
- * sysprof.c (on_about_activated): Update about box
-
- * announce-1.0.2: add this file
-
-Sun Jan 15 20:16:39 2006 Soeren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c: Add package version to spew.
-
-2006-01-13 Søren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (INTERVAL): Backport fix for HZ <=
- SAMPLES_PER_SECOND from HEAD. (Thomas de Grenier de Latour).
-
-Wed Jan 11 20:23:06 2006 Søren Sandmann <sandmann@redhat.com>
-
- * process.c (read_maps): Also make offset a gulong (Samuel
- Mimram).
-
-Mon Jan 9 09:56:46 2006 Soeren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (timer_notify): Remove START_OF_STACK
- check.
-
-Mon Jan 9 00:53:35 2006 Soeren Sandmann <sandmann@redhat.com>
-
- * process.c (read_maps): Use gulong for addresses. Bug reported by
- Martin Reddy.
-
-Tue Dec 20 12:21:16 2005 Søren Sandmann <sandmann@redhat.com>
-
- * configure.ac: Add missing backslashes, pointed out by Ralph
- Siemsen.
-
-Mon Dec 19 15:46:27 2005 Søren Sandmann <sandmann@redhat.com>
-
- * =-=-=-= Release sysprof 1.0.1 =-=-=-=
-
- * configure.ac: Bump version numbers
-
-Mon Dec 19 15:39:52 2005 Søren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (timer_notify): Make kernel module
- compile with recent kernels. Delete lots of commented out code.
-
-Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com>
-
- Fix crash pointed reported by Rudi Chiarito.
-
- * stackstash.c (stack_stash_add_trace): Just return if
- n_addrs is 0.
-
- * sysprof.c (on_read): Only trace if n_addresses != 0.
-
-Mon Oct 10 14:33:50 2005 Søren Sandmann <sandmann@redhat.com>
-
- * configure.ac: Add --disable-kernel-module option. Patch from
- Pascal Terjan.
-
-Sat Sep 17 14:35:32 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * Bump version numbers
- * README: update
- * TODO: Updates
-
-Sun Sep 4 19:38:51 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
-Tue Aug 30 16:57:33 2005 Søren Sandmann <sandmann@redhat.com>
-
- * configure.ac: Complain if we can't find /lib/modules/`uname
- -r`/build/Makefile.
-
- * process.c (process_lookup_symbol): Take an address of 0x1 to
- mean "in kernel".
-
- * module/sysprof-module.c (timer_notify): When reporting in-kernel
- time, give the current pid instead of -1.
-
- * TODO: updates
-
-Mon Aug 15 20:39:11 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * binfile.c, process.c, profile.c: Fix some warnings.
-
-Mon Aug 1 23:49:51 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (REG_INS_PTR): Add support for
- amd64/x86-64. Patch from Mike Frysinger.
-
-Sun Jul 10 10:51:52 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * binfile.c: Various minor clean-ups
-
-Sat Jul 9 23:20:39 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * binfile.c (bin_file_new): Cache BinFiles by filename.
-
- * stackstash.c (stack_stash_free): Plug leak
-
- * process.c (process_free_maps): Plug leak
-
- * module/Makefile (install): Check that depmod exists before
- running it.
-
-Sun Jun 19 15:42:34 2005 Søren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c (SAMPLES_PER_SECOND): Set to 200.
-
- * sysprof.c (on_about_activated): Add version information
-
- * configure.ac: Bump version to 0.91
-
- * README: Updates
-
-Sat Jun 18 22:45:04 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
- * configure.ac: Check for Linux 2.6.11
-
- * process.c (get_pidname): Present pid=-1 as [kernel].
-
- * module/sysprof-module.c: Use register_timer_hook() instead of
- a kernel timer. Set trace.pid to -1 if interrupt happens in
- kernel.
-
-Sun Jun 12 20:30:37 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (build_gui): Disable type-ahead search for all the
- tree views.
-
- * sysprof.c (on_object_selection_changed): Call it from here
-
- * sysprof.c (expand_descendants_tree): New function that
- determines what nodes to expand in the descendatns view.
-
-Sun Jun 12 13:37:15 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
-Thu Jun 9 13:28:33 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
-Thu May 26 01:10:45 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (on_callers_row_activated): Grab focus on the callers
- view, not the descendants view.
-
- * sysprof.c (on_read): Add a short "dead" period after a reset,
- so that 'samples' will actually be 0 for a while.
-
-Mon May 23 01:37:26 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * README: Remove comment about auto* stuff, link to
-
- http://www.daimi.au.dk/~sandmann/sysprof/
-
-Sun May 23 16:10:00 2005 Soeren Sandmann <sandmann@redhat.com>
-
- -=-=-=-=-=- Release v. 0.9 -=-=-=-=-=-
-
-Sun May 22 21:06:36 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
-Sat May 21 20:58:59 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: update
-
- * sysprof.c (on_menu_item_activated): New function.
-
- * sysprof.c (build_gui): Hook up menu items.
-
- * module/sysprof-module.c (init_module): Remove module_init/exit
- as they cause build failure on kernels < 2.6.11.
-
-Sat May 21 00:59:38 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: update
-
-Wed May 18 22:21:52 2005 Søren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c: Remove ref-counting since it didn't
- actually do any good.
-
- * sysprof.c (load_module): Use g_spawn_command_line_sync() instaed
- of system().
-
-Sun May 15 11:56:30 2005 Søren Sandmann <sandmann@redhat.com>
-
- * module/sysprof-module.c: First attempt at making module robust
- agains unloading when in use.
-
-Sun May 15 10:24:09 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * Makefile.am, module/Makefile: Do more-or-less what the automake
- manual suggests about foreign subdirectories.
-
-Sat May 14 16:36:32 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (set_application_title): Update the title bar on
- load/save
-
- * treeviewutils.c, sfile.c: Fix compiler warnings
-
- * Makefile.am: define PIXMAPDIR
-
-Sat May 14 15:49:52 2005 Søren Sandmann <sandmann@redhat.com>
-
- Auto*ify.
-
- * TODO: updates
-
- * AUTHORS, INSTALL, Makefile.am, NEWS, configure.ac: New files
-
- * module/Makefile: New file
-
- * module/sysprof-module.c, module/sysprof-module.h: Move these
- files to their own directy, as the kernel build system does not
- work very well with auto*.
-
- * sysprof.c, autogen.sh: Some auto* changes.
-
-Sun May 8 16:31:32 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: more updates
-
- * sysprof.c: Try loading the module before complaining
-
-Sun May 8 15:45:08 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (do_generate): Restore lost wake_up().
-
- * sfile.c: Comment out use of bz2.
-
- * Makefile: Add an install target. Add GLADE_DIR and PIXMAP_DIR
-
- * sysprof.c (build_gui): use GLADE_DIR and PIXMAP_DIR here.
-
- * TODO: Updates.
-
-Sat May 7 13:57:17 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sfile.c (sfile_output_free): Implement this function
-
- * sfile.c (sfile_input_free): Implement this function
-
-Fri May 6 23:38:48 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (do_generate): Another desparate hack to try
- and prevent the oops.
-
-Sat Apr 30 16:57:23 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * process.c (PAGE_SIZE): Use getpagesize()
-
- * TODO: More updates
-
-Sat Apr 30 15:44:12 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
- * sysprof-module.c (get_regs): Change the way we get registers for
- a task so that it works with 2.6.11
-
-Sat Apr 23 19:17:18 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
-Sat Apr 23 19:12:52 2005 Søren Sandmann <sandmann@redhat.com>
-
- * profile.c: Store a pointer to the root of the call tree
-
- * profile.c (profile_load): Call sfile_input_free()
-
- * sfile.c (sformat_free): Implement this function
-
-Sat Apr 23 18:38:46 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sfile.c (post_process_read_instructions): Check pointer types
-
- * sfile.c (post_process_instructions_recurse): Delete this unused function
-
-Sat Apr 23 17:49:33 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (page_readable): New function to check if the
- page is readable before reading. Noop on kernel <= 2.6.11
-
- * sysprof-module.c (get_mm, put_mm): New functions to confine
- #ifdefs.
-
-Sat Apr 23 17:48:22 2005 Søren Sandmann <sandmann@redhat.com>
-
- * Makefile (MODCFLAGS): Disable optimization as I suspect
- the oops is related to miscompilation.
-
-Fri Apr 22 00:09:16 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (read_user_space): On >= 2.6.11 check that the
- pages are present and readable before reading them.
-
-Tue Apr 19 23:26:45 2005 Kristian Høgsberg <krh@bitplanet.net>
-
- * Makefile (check): Add simple check target that runs a sanity
- check of the build environment.
-
-Sun Apr 17 00:20:41 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (on_open_clicked): Factor out some stuff in their own
- functions.
-
- * sysprof.c (load_file): Idle handler to load files given on the
- command line.
-
- * sysprof.c (main): If a filename is passed on the command line,
- load it in an idle handler.
-
-Sun Apr 17 00:19:03 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
-Sat Apr 16 19:51:48 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (read_user_space): Read a whole page at a time.
-
-Sat Apr 16 14:15:55 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: Update
-
- * sysprof-module.c (x_access_process_vm): On kernel 2.6.9 and
- later use get_task_mm()/mmput() instead of directly accessing
- task->mm.
-
-Sat Apr 16 01:54:18 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: Add backtrace for kernel oops.
-
-Fri Apr 15 16:37:45 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: updates
-
- * sysprof.c (sorry): If you hit profile when the module isn't
- loaded, pop up an annoying dialog.
-
- * sysprof-module.c: Clean-ups, remove various unused abstractions.
-
-Sat Apr 9 17:49:13 2005 Søren Sandmann <sandmann@redhat.com>
-
- * COPYING: Add a copy of the GPL
-
-Sat Apr 9 17:04:50 2005 Søren Sandmann <sandmann@redhat.com>
-
- * Makefile: Remove debug spew
-
- * *: Add copyright notices
-
-Fri Apr 8 21:30:02 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: More updates
-
-Fri Apr 8 20:48:58 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (build_gui): Load the icon, hook up "about"
- activation.
-
- * sysprof.c (on_about_activated): New function. Show an about
- dialog.
-
- * sysprof.c (struct Application): Add an icon field
-
- * TODO: Updates
-
- * sysprof-icon.png: Icon, drawn by Diana Fong
-
-Tue Apr 5 23:01:02 2005 Søren Sandmann <sandmann@redhat.com>
-
- * binfile.c (read_symbols): Put back the weird loop, and stop
- pretending I understand this. This time use SEC_ALLOC instead of
- SEC_LOAD.
-
-Tue Apr 5 20:13:44 2005 Søren Sandmann <sandmann@redhat.com>
-
- * process.c (process_ensure_map): Add commented out debug spew.
-
- * process.c (process_lookup_symbol): Remove all should_offset()
- function and all references to it.
-
- * binfile.c (bin_file_lookup_symbol): Document that address must
- be in file coordinates.
-
- * binfile.c (read_symbols): Remove misguided code that tried to
- guess the load address of the file. Instead, do all computations
- in "file coordinates". Also fix a memory leak. Add commented out
- debug spew.
-
- * binfile.c (separate_debug_file_exists): Fix signedness
-
-Tue Apr 5 14:34:43 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (x_access_process_vm): Make it compile with
- kernel 2.6.11
-
- * TODO: updates
-
-Mon Apr 4 00:57:11 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof.c: Busy cursors in many more places.
-
- * TODO: updates
-
-Sun Apr 3 23:28:45 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (do_generate): Re-schedule the timeout here
- instead of in on_timer().
-
- * sysprof-module.c (on_timer): Only block tasks in the
- TASK_RUNNING state.
-
-Sun Apr 3 17:03:33 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (queue_generate_stack_trace): Put current
- process to sleep.
-
- * sysprof-module.c (do_generate): Wake up the traced process
-
-Thu Mar 31 23:09:09 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (build_gui): Remove stray %
-
-Thu Mar 31 21:18:13 2005 Søren Sandmann <sandmann@redhat.com>
-
- * treeviewutils.c (add_double_format_column): Right justify numbers
-
- * sysprof.c (build_gui): Add a space after the numbers
-
- * sysprof.c (on_callers_row_activated): Focus object view
-
- * sysprof.c (on_descendants_row_activated): Focus new descendants
- tree.
-
-Thu Mar 31 19:51:51 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c (do_generate): Walk all threads, not just all
- processes.
-
- * TODO: Add disk profiling ideas
-
-Thu Mar 31 00:19:47 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (set_busy): Make this function work
-
- * sysprof.c (on_profile_toggled): Use it here
-
- * sysprof.c (on_object_selection_changed): And here
-
- * profile.c (add_trace_to_tree): Use GPtrArrays instead of
- GHashTable and GList.
-
-Mon Mar 28 11:09:02 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: updates
-
-Sat Mar 26 19:26:52 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c: Show the right number of samples afte Open; remove
- shadows from menu bars and toolbars; some other tweaks.
-
-Sat Mar 26 11:26:00 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * TODO: Updates
-
- * sfile.c (add_string): Use g_markup_escape_text() to escape the
- string before adding it to the file.
-
- * sysprof.c (empty_file_descriptor): New function to make sure
- samples generated before profiling started are ignored.
- (set_busy): New commented out function to set a busy cursor.
-
-Fri Mar 25 21:31:08 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (update_sensitivity): Comment out sensitivity of reset button.
-
-Fri Mar 25 21:25:31 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (get_current_object): Return NULL if nothing is
- selected.
-
-Fri Mar 25 20:54:08 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: More updates
-
-Fri Mar 25 20:25:44 2005 Søren Sandmann <sandmann@redhat.com>
-
- * README: Require gtk+ 2.6
-
- * treeviewutils.c (add_plain_text_column): Ellipsisize text columns.
-
-Fri Mar 25 19:39:24 2005 Søren Sandmann <sandmann@redhat.com>
-
- * TODO: Remove "loading and saving"
-
- * sysprof.glade: Add ellipsises to Open and Save menu items.
-
- * sysprof.c (overwrite_file): Add this function, cutted-and-pasted
- from evince.
-
- * sysprof.c (on_save_as_clicked, on_open_clicked): Use
- GtkFileChoosers to pick the names.
-
- * sysprof.c: Various GUI updates.
-
-Fri Mar 25 19:36:28 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sfile.c (bz2_compress): Add this function Don't actually make
- any produce use of it.
-
- * profile.c (make_hash_table): Get rid of warning
-
-Thu Mar 24 19:09:33 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c: Various GUI updates
-
- * TODO: update
-
- * sfile.[ch] (sformat_new_optional): Add some notes about an
- "optional" construction.
-
-Wed Mar 23 00:04:07 2005 Soeren Sandmann <sandmann@redhat.com>
-
- Primitive loading and saving.
-
- * sysprof.c (on_open_clicked): Hook up loading.
-
- * sfile.c: Add a copy of g_file_replace() from glib CVS HEAD.
-
- * sfile.c (add_string): Escape and quote the string
-
- * sfile.c (sfile_load): Initialize current_instruction and
- instructions_by_location
-
- * sfile.c (post_process_instructions_recurse): Handle NULL
- pointers properly.
-
- * sfile.c (handle_begin_element, handle_end_element, handle_text):
- Move error handling here from state_transition_begin/text/end.
-
- * sfile.c (handle_text): Discard whitespace-only strings
-
- * sfile.c (sfile_get_pointer, sfile_get_integer,
- sfile_get_string): expect both begin, value, and end transitions.
-
- * sfile.c (hook_up_pointers): Only treat instructions as pointer
- values when they are. Handle NULL targets properly.
-
- * sfile.c (get_number): Fix a few read-freed-data bugs
-
- * profile.c (profile_load): Call sfile_end_get() for the profile;
- build the nodes_by_objects hash table. Build the call tree.
-
- * profile.c (create_format): Don't store next pointer, but do
- store total, self and toplevel.
-
- * profile.c (make_hash_table): New function to build
- nodes_by_object hashtable from loaded data
-
-Sat Mar 12 11:05:19 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sysprof-module.c: Fix small bug in add_timeout()
- * sysprof.c (build_gui): More descriptive tree labels
- * TODO: update
-
-Thu Mar 10 16:37:52 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c (build_gui): s/Cummulative/Cumulative/. Pointed out by
- Ian McIntosh.
-
-Mon Mar 7 14:47:09 2005 Søren Sandmann <sandmann@redhat.com>
-
- * README: Add note that you need to compile the module with
- the same compiler that compiled the kernel.
-
-Sun Mar 6 22:56:21 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sfile.c: Generate id's for objects and pointers.
-
-Sat Mar 5 01:09:33 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * sfile.c: Bug fixes. Add actual generation.
-
-Fri Mar 4 13:47:13 2005 Søren Sandmann <sandmann@redhat.com>
-
- * sysprof.c: Remove include of non-existing tracing.h
-
-Thu Mar 3 23:48:13 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * profile.c (profile_load): Write this function.
-
- * sfile.c: Add support for user defined record and list types.
- Simplify logic a lot.
-
-Wed Mar 2 23:39:50 2005 Soeren Sandmann <sandmann@redhat.com>
-
- * profile.[ch], sfile.[ch]: Experiment with a
- file-format-description format.
-
- * sysprof.c: Add commented out code using /proc/ based
- timeout.
-
-Fri Jan 21 11:23:54 2005 Søren Sandmann <sandmann@redhat.com>
-
- * README: Some updates - add note about SMP kernels.
-
- * sysprof-module.c: Go back to just sampling the current
- process.
-
- * ChangeLog: I guess these do make sense, so start one.
diff --git a/Makefile.am b/Makefile.am
index a9ee4b2..6436ab9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,47 +1,122 @@
-SUBDIRS = $(MODULE_SUBDIR)
-DIST_SUBDIRS = module
-
-bin_PROGRAMS = sysprof
-pkgdata_DATA = sysprof.glade sysprof-icon.png
-
-sysprof_SOURCES = \
- binfile.h \
- binfile.c \
- process.h \
- process.c \
- profile.h \
- profile.c \
- sfile.h \
- sfile.c \
- stackstash.h \
- stackstash.c \
- module/sysprof-module.h \
- sysprof.c \
- treeviewutils.h \
- treeviewutils.c \
- watch.h \
- watch.c
-
-sysprof_LDADD = $(DEP_LIBS)
-
-INCLUDES = \
- $(DEP_CFLAGS) \
- -DDATADIR=\"$(pkgdatadir)\" \
- -DPIXMAPDIR=\"$(datadir)/pixmaps\"
-
-# memprof.desktop
-# memprof.spec.in
-
-EXTRA_DIST = \
- sysprof.glade \
- sysprof-icon.png \
- module/sysprof-module.c \
- module/sysprof-module.h \
- module/Makefile
+#SUBDIRS = $(MODULE_SUBDIR)
+#DIST_SUBDIRS = module
+
+bin_PROGRAMS = sysprof-cli
+
+if BUILD_GUI
+bin_PROGRAMS += sysprof
+endif
+
+SYSPROF_CORE = \
+ binfile.h \
+ binfile.c \
+ collector.c \
+ collector.h \
+ demangle.c \
+ elfparser.c \
+ elfparser.h \
+ profile.h \
+ profile.c \
+ sfile.h \
+ sfile.c \
+ sformat.h \
+ sformat.c \
+ stackstash.h \
+ stackstash.c \
+ tracker.h \
+ tracker.c \
+ unwind.h \
+ unwind.c \
+ watch.h \
+ watch.c \
+ \
+ perf_counter.h \
+ util.h
+
+# module/sysprof-module.h
+
+#
+# GUI version
+#
+if BUILD_GUI
+
+sysprof_SOURCES = \
+ $(SYSPROF_CORE) \
+ footreestore.c \
+ footreestore.h \
+ footreedatalist.h \
+ footreedatalist.c \
+ treeviewutils.h \
+ treeviewutils.c \
+ sysprof.c
+
+sysprof_CPPFLAGS = \
+ $(GUI_DEP_CFLAGS) \
+ -DDATADIR=\"$(pkgdatadir)\" \
+ -DPIXMAPDIR=\"$(pixmapsdir)\"
+
+sysprof_LDADD = $(GUI_DEP_LIBS)
+
+endif
+
+udevdir = $(sysconfdir)/udev/rules.d
+dist_udev_DATA = 60-sysprof.rules
pixmapsdir = $(datadir)/pixmaps
-pixmaps_DATA = sysprof-icon.png
-insert-module:
- /sbin/modprobe -r sysprof-module
- /sbin/modprobe sysprof-module
+dist_pkgdata_DATA = sysprof.glade
+dist_pixmaps_DATA = sysprof-icon-16.png sysprof-icon-24.png sysprof-icon-32.png sysprof-icon-48.png
+
+#
+# Command line version
+#
+
+sysprof_cli_SOURCES = \
+ $(SYSPROF_CORE) \
+ signal-handler.h \
+ signal-handler.c \
+ sysprof-cli.c
+
+sysprof_cli_CPPFLAGS = \
+ $(CORE_DEP_CFLAGS)
+
+sysprof_cli_LDADD = $(CORE_DEP_LIBS)
+
+#
+# Module stuff
+#
+
+#EXTRA_DIST = \
+# module/sysprof-module.c \
+# module/sysprof-module.h \
+# module/Makefile
+
+#insert-module:
+# /sbin/modprobe -r sysprof-module
+# /sbin/modprobe sysprof-module
+
+#
+# Test programs
+#
+noinst_PROGRAMS = testelf testunwind
+
+# testunwind
+testunwind_SOURCES = \
+ testunwind.c \
+ demangle.c \
+ elfparser.c \
+ elfparser.h \
+ unwind.c \
+ unwind.h
+testunwind_CPPFLAGS = $(CORE_DEP_CFLAGS)
+testunwind_LDADD = $(CORE_DEP_LIBS)
+
+# testelf
+testelf_SOURCES = \
+ testelf.c \
+ demangle.c \
+ elfparser.c \
+ elfparser.h
+
+testelf_CPPFLAGS = $(CORE_DEP_CFLAGS)
+testelf_LDADD = $(CORE_DEP_LIBS)
diff --git a/Makefile.in b/Makefile.in
index 42b7754..5f518ac 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -14,6 +14,9 @@
@SET_MAKE@
+#SUBDIRS = $(MODULE_SUBDIR)
+#DIST_SUBDIRS = module
+
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
@@ -35,6 +38,7 @@ POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
+@BUILD_GUI_TRUE@am__append_1 = sysprof
ACLOCAL = @ACLOCAL@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
@@ -43,19 +47,23 @@ AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
+BUILD_GUI_FALSE = @BUILD_GUI_FALSE@
+BUILD_GUI_TRUE = @BUILD_GUI_TRUE@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
+CORE_DEP_CFLAGS = @CORE_DEP_CFLAGS@
+CORE_DEP_LIBS = @CORE_DEP_LIBS@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
-DEP_CFLAGS = @DEP_CFLAGS@
-DEP_LIBS = @DEP_LIBS@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EXEEXT = @EXEEXT@
+GUI_DEP_CFLAGS = @GUI_DEP_CFLAGS@
+GUI_DEP_LIBS = @GUI_DEP_LIBS@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
@@ -111,95 +119,236 @@ sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
-SUBDIRS = $(MODULE_SUBDIR)
-DIST_SUBDIRS = module
-
-bin_PROGRAMS = sysprof
-pkgdata_DATA = sysprof.glade sysprof-icon.png
-
-sysprof_SOURCES = \
- binfile.h \
- binfile.c \
- process.h \
- process.c \
- profile.h \
- profile.c \
- sfile.h \
- sfile.c \
- stackstash.h \
- stackstash.c \
- module/sysprof-module.h \
- sysprof.c \
- treeviewutils.h \
- treeviewutils.c \
- watch.h \
- watch.c
-
-
-sysprof_LDADD = $(DEP_LIBS)
-
-INCLUDES = \
- $(DEP_CFLAGS) \
- -DDATADIR=\"$(pkgdatadir)\" \
- -DPIXMAPDIR=\"$(datadir)/pixmaps\"
-
-
-
-# memprof.desktop
-# memprof.spec.in
-EXTRA_DIST = \
- sysprof.glade \
- sysprof-icon.png \
- module/sysprof-module.c \
- module/sysprof-module.h \
- module/Makefile
+bin_PROGRAMS = sysprof-cli $(am__append_1)
+
+SYSPROF_CORE = \
+ binfile.h \
+ binfile.c \
+ collector.c \
+ collector.h \
+ demangle.c \
+ elfparser.c \
+ elfparser.h \
+ profile.h \
+ profile.c \
+ sfile.h \
+ sfile.c \
+ sformat.h \
+ sformat.c \
+ stackstash.h \
+ stackstash.c \
+ tracker.h \
+ tracker.c \
+ unwind.h \
+ unwind.c \
+ watch.h \
+ watch.c \
+ \
+ perf_counter.h \
+ util.h
+
+
+
+# module/sysprof-module.h
+
+#
+# GUI version
+#
+@BUILD_GUI_TRUE@sysprof_SOURCES = \
+@BUILD_GUI_TRUE@ $(SYSPROF_CORE) \
+@BUILD_GUI_TRUE@ footreestore.c \
+@BUILD_GUI_TRUE@ footreestore.h \
+@BUILD_GUI_TRUE@ footreedatalist.h \
+@BUILD_GUI_TRUE@ footreedatalist.c \
+@BUILD_GUI_TRUE@ treeviewutils.h \
+@BUILD_GUI_TRUE@ treeviewutils.c \
+@BUILD_GUI_TRUE@ sysprof.c
+
+
+@BUILD_GUI_TRUE@sysprof_CPPFLAGS = \
+@BUILD_GUI_TRUE@ $(GUI_DEP_CFLAGS) \
+@BUILD_GUI_TRUE@ -DDATADIR=\"$(pkgdatadir)\" \
+@BUILD_GUI_TRUE@ -DPIXMAPDIR=\"$(pixmapsdir)\"
+
+
+@BUILD_GUI_TRUE@sysprof_LDADD = $(GUI_DEP_LIBS)
+
+udevdir = $(sysconfdir)/udev/rules.d
+dist_udev_DATA = 60-sysprof.rules
pixmapsdir = $(datadir)/pixmaps
-pixmaps_DATA = sysprof-icon.png
+
+dist_pkgdata_DATA = sysprof.glade
+dist_pixmaps_DATA = sysprof-icon-16.png sysprof-icon-24.png sysprof-icon-32.png sysprof-icon-48.png
+
+
+#
+# Command line version
+#
+sysprof_cli_SOURCES = \
+ $(SYSPROF_CORE) \
+ signal-handler.h \
+ signal-handler.c \
+ sysprof-cli.c
+
+
+sysprof_cli_CPPFLAGS = \
+ $(CORE_DEP_CFLAGS)
+
+
+sysprof_cli_LDADD = $(CORE_DEP_LIBS)
+
+#
+# Module stuff
+#
+
+#EXTRA_DIST = \
+# module/sysprof-module.c \
+# module/sysprof-module.h \
+# module/Makefile
+
+#insert-module:
+# /sbin/modprobe -r sysprof-module
+# /sbin/modprobe sysprof-module
+
+#
+# Test programs
+#
+noinst_PROGRAMS = testelf testunwind
+
+# testunwind
+testunwind_SOURCES = \
+ testunwind.c \
+ demangle.c \
+ elfparser.c \
+ elfparser.h \
+ unwind.c \
+ unwind.h
+
+testunwind_CPPFLAGS = $(CORE_DEP_CFLAGS)
+testunwind_LDADD = $(CORE_DEP_LIBS)
+
+# testelf
+testelf_SOURCES = \
+ testelf.c \
+ demangle.c \
+ elfparser.c \
+ elfparser.h
+
+
+testelf_CPPFLAGS = $(CORE_DEP_CFLAGS)
+testelf_LDADD = $(CORE_DEP_LIBS)
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
-bin_PROGRAMS = sysprof$(EXEEXT)
-PROGRAMS = $(bin_PROGRAMS)
-
-am_sysprof_OBJECTS = binfile.$(OBJEXT) process.$(OBJEXT) \
- profile.$(OBJEXT) sfile.$(OBJEXT) stackstash.$(OBJEXT) \
- sysprof.$(OBJEXT) treeviewutils.$(OBJEXT) watch.$(OBJEXT)
+@BUILD_GUI_TRUE@bin_PROGRAMS = sysprof-cli$(EXEEXT) sysprof$(EXEEXT)
+@BUILD_GUI_FALSE@bin_PROGRAMS = sysprof-cli$(EXEEXT)
+noinst_PROGRAMS = testelf$(EXEEXT) testunwind$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+
+am__sysprof_SOURCES_DIST = binfile.h binfile.c collector.c collector.h \
+ demangle.c elfparser.c elfparser.h profile.h profile.c sfile.h \
+ sfile.c sformat.h sformat.c stackstash.h stackstash.c tracker.h \
+ tracker.c unwind.h unwind.c watch.h watch.c perf_counter.h \
+ util.h footreestore.c footreestore.h footreedatalist.h \
+ footreedatalist.c treeviewutils.h treeviewutils.c sysprof.c
+am__objects_1 = sysprof-binfile.$(OBJEXT) sysprof-collector.$(OBJEXT) \
+ sysprof-demangle.$(OBJEXT) sysprof-elfparser.$(OBJEXT) \
+ sysprof-profile.$(OBJEXT) sysprof-sfile.$(OBJEXT) \
+ sysprof-sformat.$(OBJEXT) sysprof-stackstash.$(OBJEXT) \
+ sysprof-tracker.$(OBJEXT) sysprof-unwind.$(OBJEXT) \
+ sysprof-watch.$(OBJEXT)
+@BUILD_GUI_TRUE@am_sysprof_OBJECTS = $(am__objects_1) \
+@BUILD_GUI_TRUE@ sysprof-footreestore.$(OBJEXT) \
+@BUILD_GUI_TRUE@ sysprof-footreedatalist.$(OBJEXT) \
+@BUILD_GUI_TRUE@ sysprof-treeviewutils.$(OBJEXT) \
+@BUILD_GUI_TRUE@ sysprof-sysprof.$(OBJEXT)
sysprof_OBJECTS = $(am_sysprof_OBJECTS)
-sysprof_DEPENDENCIES =
+@BUILD_GUI_TRUE@sysprof_DEPENDENCIES =
+@BUILD_GUI_FALSE@sysprof_DEPENDENCIES =
sysprof_LDFLAGS =
+am__objects_2 = sysprof_cli-binfile.$(OBJEXT) \
+ sysprof_cli-collector.$(OBJEXT) sysprof_cli-demangle.$(OBJEXT) \
+ sysprof_cli-elfparser.$(OBJEXT) sysprof_cli-profile.$(OBJEXT) \
+ sysprof_cli-sfile.$(OBJEXT) sysprof_cli-sformat.$(OBJEXT) \
+ sysprof_cli-stackstash.$(OBJEXT) sysprof_cli-tracker.$(OBJEXT) \
+ sysprof_cli-unwind.$(OBJEXT) sysprof_cli-watch.$(OBJEXT)
+am_sysprof_cli_OBJECTS = $(am__objects_2) \
+ sysprof_cli-signal-handler.$(OBJEXT) \
+ sysprof_cli-sysprof-cli.$(OBJEXT)
+sysprof_cli_OBJECTS = $(am_sysprof_cli_OBJECTS)
+sysprof_cli_DEPENDENCIES =
+sysprof_cli_LDFLAGS =
+am_testelf_OBJECTS = testelf-testelf.$(OBJEXT) \
+ testelf-demangle.$(OBJEXT) testelf-elfparser.$(OBJEXT)
+testelf_OBJECTS = $(am_testelf_OBJECTS)
+testelf_DEPENDENCIES =
+testelf_LDFLAGS =
+am_testunwind_OBJECTS = testunwind-testunwind.$(OBJEXT) \
+ testunwind-demangle.$(OBJEXT) testunwind-elfparser.$(OBJEXT) \
+ testunwind-unwind.$(OBJEXT)
+testunwind_OBJECTS = $(am_testunwind_OBJECTS)
+testunwind_DEPENDENCIES =
+testunwind_LDFLAGS =
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
-@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/binfile.Po ./$(DEPDIR)/process.Po \
-@AMDEP_TRUE@ ./$(DEPDIR)/profile.Po ./$(DEPDIR)/sfile.Po \
-@AMDEP_TRUE@ ./$(DEPDIR)/stackstash.Po ./$(DEPDIR)/sysprof.Po \
-@AMDEP_TRUE@ ./$(DEPDIR)/treeviewutils.Po ./$(DEPDIR)/watch.Po
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/sysprof-binfile.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-collector.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-demangle.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-elfparser.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-footreedatalist.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-footreestore.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-profile.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-sfile.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-sformat.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-stackstash.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-sysprof.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-tracker.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-treeviewutils.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-unwind.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof-watch.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-binfile.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-collector.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-demangle.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-elfparser.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-profile.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-sfile.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-sformat.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-signal-handler.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-stackstash.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-sysprof-cli.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-tracker.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-unwind.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/sysprof_cli-watch.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testelf-demangle.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testelf-elfparser.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testelf-testelf.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testunwind-demangle.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testunwind-elfparser.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testunwind-testunwind.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/testunwind-unwind.Po
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
-DIST_SOURCES = $(sysprof_SOURCES)
-DATA = $(pixmaps_DATA) $(pkgdata_DATA)
+DIST_SOURCES = $(am__sysprof_SOURCES_DIST) $(sysprof_cli_SOURCES) \
+ $(testelf_SOURCES) $(testunwind_SOURCES)
+DATA = $(dist_pixmaps_DATA) $(dist_pkgdata_DATA) $(dist_udev_DATA)
-
-RECURSIVE_TARGETS = info-recursive dvi-recursive pdf-recursive \
- ps-recursive install-info-recursive uninstall-info-recursive \
- all-recursive install-data-recursive install-exec-recursive \
- installdirs-recursive install-recursive uninstall-recursive \
- check-recursive installcheck-recursive
-DIST_COMMON = README $(srcdir)/Makefile.in $(srcdir)/configure AUTHORS \
- COPYING ChangeLog INSTALL Makefile.am NEWS TODO aclocal.m4 \
- compile config.h.in configure configure.ac depcomp install-sh \
- missing mkinstalldirs
-SOURCES = $(sysprof_SOURCES)
+DIST_COMMON = README $(dist_pixmaps_DATA) $(dist_pkgdata_DATA) \
+ $(dist_udev_DATA) $(srcdir)/Makefile.in $(srcdir)/configure \
+ AUTHORS COPYING ChangeLog INSTALL Makefile.am NEWS TODO \
+ aclocal.m4 compile config.h.in configure configure.ac depcomp \
+ install-sh missing mkinstalldirs
+SOURCES = $(sysprof_SOURCES) $(sysprof_cli_SOURCES) $(testelf_SOURCES) $(testunwind_SOURCES)
all: config.h
- $(MAKE) $(AM_MAKEFLAGS) all-recursive
+ $(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
.SUFFIXES: .c .o .obj
@@ -260,9 +409,21 @@ uninstall-binPROGRAMS:
clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
sysprof$(EXEEXT): $(sysprof_OBJECTS) $(sysprof_DEPENDENCIES)
@rm -f sysprof$(EXEEXT)
$(LINK) $(sysprof_LDFLAGS) $(sysprof_OBJECTS) $(sysprof_LDADD) $(LIBS)
+sysprof-cli$(EXEEXT): $(sysprof_cli_OBJECTS) $(sysprof_cli_DEPENDENCIES)
+ @rm -f sysprof-cli$(EXEEXT)
+ $(LINK) $(sysprof_cli_LDFLAGS) $(sysprof_cli_OBJECTS) $(sysprof_cli_LDADD) $(LIBS)
+testelf$(EXEEXT): $(testelf_OBJECTS) $(testelf_DEPENDENCIES)
+ @rm -f testelf$(EXEEXT)
+ $(LINK) $(testelf_LDFLAGS) $(testelf_OBJECTS) $(testelf_LDADD) $(LIBS)
+testunwind$(EXEEXT): $(testunwind_OBJECTS) $(testunwind_DEPENDENCIES)
+ @rm -f testunwind$(EXEEXT)
+ $(LINK) $(testunwind_LDFLAGS) $(testunwind_OBJECTS) $(testunwind_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT) core *.core
@@ -270,14 +431,41 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/binfile.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sfile.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stackstash.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/treeviewutils.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-binfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-collector.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-demangle.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-elfparser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-footreedatalist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-footreestore.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-profile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-sfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-sformat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-stackstash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-sysprof.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-tracker.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-treeviewutils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-unwind.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof-watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-binfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-collector.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-demangle.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-elfparser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-profile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-sfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-sformat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-signal-handler.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-stackstash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-sysprof-cli.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-tracker.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-unwind.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysprof_cli-watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testelf-demangle.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testelf-elfparser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testelf-testelf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testunwind-demangle.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testunwind-elfparser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testunwind-testunwind.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testunwind-unwind.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \
@@ -300,101 +488,830 @@ distclean-compile:
@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(COMPILE) -c `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'; fi`
+
+sysprof-binfile.o: binfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-binfile.o -MD -MP -MF "$(DEPDIR)/sysprof-binfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-binfile.o `test -f 'binfile.c' || echo '$(srcdir)/'`binfile.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-binfile.Tpo" "$(DEPDIR)/sysprof-binfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-binfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='binfile.c' object='sysprof-binfile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-binfile.Po' tmpdepfile='$(DEPDIR)/sysprof-binfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-binfile.o `test -f 'binfile.c' || echo '$(srcdir)/'`binfile.c
+
+sysprof-binfile.obj: binfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-binfile.obj -MD -MP -MF "$(DEPDIR)/sysprof-binfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-binfile.obj `if test -f 'binfile.c'; then $(CYGPATH_W) 'binfile.c'; else $(CYGPATH_W) '$(srcdir)/binfile.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-binfile.Tpo" "$(DEPDIR)/sysprof-binfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-binfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='binfile.c' object='sysprof-binfile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-binfile.Po' tmpdepfile='$(DEPDIR)/sysprof-binfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-binfile.obj `if test -f 'binfile.c'; then $(CYGPATH_W) 'binfile.c'; else $(CYGPATH_W) '$(srcdir)/binfile.c'; fi`
+
+sysprof-collector.o: collector.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-collector.o -MD -MP -MF "$(DEPDIR)/sysprof-collector.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-collector.o `test -f 'collector.c' || echo '$(srcdir)/'`collector.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-collector.Tpo" "$(DEPDIR)/sysprof-collector.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-collector.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='collector.c' object='sysprof-collector.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-collector.Po' tmpdepfile='$(DEPDIR)/sysprof-collector.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-collector.o `test -f 'collector.c' || echo '$(srcdir)/'`collector.c
+
+sysprof-collector.obj: collector.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-collector.obj -MD -MP -MF "$(DEPDIR)/sysprof-collector.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-collector.obj `if test -f 'collector.c'; then $(CYGPATH_W) 'collector.c'; else $(CYGPATH_W) '$(srcdir)/collector.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-collector.Tpo" "$(DEPDIR)/sysprof-collector.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-collector.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='collector.c' object='sysprof-collector.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-collector.Po' tmpdepfile='$(DEPDIR)/sysprof-collector.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-collector.obj `if test -f 'collector.c'; then $(CYGPATH_W) 'collector.c'; else $(CYGPATH_W) '$(srcdir)/collector.c'; fi`
+
+sysprof-demangle.o: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-demangle.o -MD -MP -MF "$(DEPDIR)/sysprof-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-demangle.Tpo" "$(DEPDIR)/sysprof-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='sysprof-demangle.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-demangle.Po' tmpdepfile='$(DEPDIR)/sysprof-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c
+
+sysprof-demangle.obj: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-demangle.obj -MD -MP -MF "$(DEPDIR)/sysprof-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-demangle.Tpo" "$(DEPDIR)/sysprof-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='sysprof-demangle.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-demangle.Po' tmpdepfile='$(DEPDIR)/sysprof-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`
+
+sysprof-elfparser.o: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-elfparser.o -MD -MP -MF "$(DEPDIR)/sysprof-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-elfparser.Tpo" "$(DEPDIR)/sysprof-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='sysprof-elfparser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-elfparser.Po' tmpdepfile='$(DEPDIR)/sysprof-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c
+
+sysprof-elfparser.obj: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-elfparser.obj -MD -MP -MF "$(DEPDIR)/sysprof-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-elfparser.Tpo" "$(DEPDIR)/sysprof-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='sysprof-elfparser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-elfparser.Po' tmpdepfile='$(DEPDIR)/sysprof-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`
+
+sysprof-profile.o: profile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-profile.o -MD -MP -MF "$(DEPDIR)/sysprof-profile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-profile.o `test -f 'profile.c' || echo '$(srcdir)/'`profile.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-profile.Tpo" "$(DEPDIR)/sysprof-profile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-profile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='profile.c' object='sysprof-profile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-profile.Po' tmpdepfile='$(DEPDIR)/sysprof-profile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-profile.o `test -f 'profile.c' || echo '$(srcdir)/'`profile.c
+
+sysprof-profile.obj: profile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-profile.obj -MD -MP -MF "$(DEPDIR)/sysprof-profile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-profile.obj `if test -f 'profile.c'; then $(CYGPATH_W) 'profile.c'; else $(CYGPATH_W) '$(srcdir)/profile.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-profile.Tpo" "$(DEPDIR)/sysprof-profile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-profile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='profile.c' object='sysprof-profile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-profile.Po' tmpdepfile='$(DEPDIR)/sysprof-profile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-profile.obj `if test -f 'profile.c'; then $(CYGPATH_W) 'profile.c'; else $(CYGPATH_W) '$(srcdir)/profile.c'; fi`
+
+sysprof-sfile.o: sfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-sfile.o -MD -MP -MF "$(DEPDIR)/sysprof-sfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-sfile.o `test -f 'sfile.c' || echo '$(srcdir)/'`sfile.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-sfile.Tpo" "$(DEPDIR)/sysprof-sfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-sfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sfile.c' object='sysprof-sfile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-sfile.Po' tmpdepfile='$(DEPDIR)/sysprof-sfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-sfile.o `test -f 'sfile.c' || echo '$(srcdir)/'`sfile.c
+
+sysprof-sfile.obj: sfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-sfile.obj -MD -MP -MF "$(DEPDIR)/sysprof-sfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-sfile.obj `if test -f 'sfile.c'; then $(CYGPATH_W) 'sfile.c'; else $(CYGPATH_W) '$(srcdir)/sfile.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-sfile.Tpo" "$(DEPDIR)/sysprof-sfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-sfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sfile.c' object='sysprof-sfile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-sfile.Po' tmpdepfile='$(DEPDIR)/sysprof-sfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-sfile.obj `if test -f 'sfile.c'; then $(CYGPATH_W) 'sfile.c'; else $(CYGPATH_W) '$(srcdir)/sfile.c'; fi`
+
+sysprof-sformat.o: sformat.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-sformat.o -MD -MP -MF "$(DEPDIR)/sysprof-sformat.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-sformat.o `test -f 'sformat.c' || echo '$(srcdir)/'`sformat.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-sformat.Tpo" "$(DEPDIR)/sysprof-sformat.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-sformat.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sformat.c' object='sysprof-sformat.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-sformat.Po' tmpdepfile='$(DEPDIR)/sysprof-sformat.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-sformat.o `test -f 'sformat.c' || echo '$(srcdir)/'`sformat.c
+
+sysprof-sformat.obj: sformat.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-sformat.obj -MD -MP -MF "$(DEPDIR)/sysprof-sformat.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-sformat.obj `if test -f 'sformat.c'; then $(CYGPATH_W) 'sformat.c'; else $(CYGPATH_W) '$(srcdir)/sformat.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-sformat.Tpo" "$(DEPDIR)/sysprof-sformat.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-sformat.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sformat.c' object='sysprof-sformat.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-sformat.Po' tmpdepfile='$(DEPDIR)/sysprof-sformat.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-sformat.obj `if test -f 'sformat.c'; then $(CYGPATH_W) 'sformat.c'; else $(CYGPATH_W) '$(srcdir)/sformat.c'; fi`
+
+sysprof-stackstash.o: stackstash.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-stackstash.o -MD -MP -MF "$(DEPDIR)/sysprof-stackstash.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-stackstash.o `test -f 'stackstash.c' || echo '$(srcdir)/'`stackstash.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-stackstash.Tpo" "$(DEPDIR)/sysprof-stackstash.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-stackstash.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stackstash.c' object='sysprof-stackstash.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-stackstash.Po' tmpdepfile='$(DEPDIR)/sysprof-stackstash.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-stackstash.o `test -f 'stackstash.c' || echo '$(srcdir)/'`stackstash.c
+
+sysprof-stackstash.obj: stackstash.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-stackstash.obj -MD -MP -MF "$(DEPDIR)/sysprof-stackstash.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-stackstash.obj `if test -f 'stackstash.c'; then $(CYGPATH_W) 'stackstash.c'; else $(CYGPATH_W) '$(srcdir)/stackstash.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-stackstash.Tpo" "$(DEPDIR)/sysprof-stackstash.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-stackstash.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stackstash.c' object='sysprof-stackstash.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-stackstash.Po' tmpdepfile='$(DEPDIR)/sysprof-stackstash.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-stackstash.obj `if test -f 'stackstash.c'; then $(CYGPATH_W) 'stackstash.c'; else $(CYGPATH_W) '$(srcdir)/stackstash.c'; fi`
+
+sysprof-tracker.o: tracker.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-tracker.o -MD -MP -MF "$(DEPDIR)/sysprof-tracker.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-tracker.o `test -f 'tracker.c' || echo '$(srcdir)/'`tracker.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-tracker.Tpo" "$(DEPDIR)/sysprof-tracker.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-tracker.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tracker.c' object='sysprof-tracker.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-tracker.Po' tmpdepfile='$(DEPDIR)/sysprof-tracker.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-tracker.o `test -f 'tracker.c' || echo '$(srcdir)/'`tracker.c
+
+sysprof-tracker.obj: tracker.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-tracker.obj -MD -MP -MF "$(DEPDIR)/sysprof-tracker.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-tracker.obj `if test -f 'tracker.c'; then $(CYGPATH_W) 'tracker.c'; else $(CYGPATH_W) '$(srcdir)/tracker.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-tracker.Tpo" "$(DEPDIR)/sysprof-tracker.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-tracker.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tracker.c' object='sysprof-tracker.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-tracker.Po' tmpdepfile='$(DEPDIR)/sysprof-tracker.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-tracker.obj `if test -f 'tracker.c'; then $(CYGPATH_W) 'tracker.c'; else $(CYGPATH_W) '$(srcdir)/tracker.c'; fi`
+
+sysprof-unwind.o: unwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-unwind.o -MD -MP -MF "$(DEPDIR)/sysprof-unwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-unwind.o `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-unwind.Tpo" "$(DEPDIR)/sysprof-unwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-unwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unwind.c' object='sysprof-unwind.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-unwind.Po' tmpdepfile='$(DEPDIR)/sysprof-unwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-unwind.o `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c
+
+sysprof-unwind.obj: unwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-unwind.obj -MD -MP -MF "$(DEPDIR)/sysprof-unwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-unwind.obj `if test -f 'unwind.c'; then $(CYGPATH_W) 'unwind.c'; else $(CYGPATH_W) '$(srcdir)/unwind.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-unwind.Tpo" "$(DEPDIR)/sysprof-unwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-unwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unwind.c' object='sysprof-unwind.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-unwind.Po' tmpdepfile='$(DEPDIR)/sysprof-unwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-unwind.obj `if test -f 'unwind.c'; then $(CYGPATH_W) 'unwind.c'; else $(CYGPATH_W) '$(srcdir)/unwind.c'; fi`
+
+sysprof-watch.o: watch.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-watch.o -MD -MP -MF "$(DEPDIR)/sysprof-watch.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-watch.o `test -f 'watch.c' || echo '$(srcdir)/'`watch.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-watch.Tpo" "$(DEPDIR)/sysprof-watch.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-watch.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='watch.c' object='sysprof-watch.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-watch.Po' tmpdepfile='$(DEPDIR)/sysprof-watch.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-watch.o `test -f 'watch.c' || echo '$(srcdir)/'`watch.c
+
+sysprof-watch.obj: watch.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-watch.obj -MD -MP -MF "$(DEPDIR)/sysprof-watch.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-watch.obj `if test -f 'watch.c'; then $(CYGPATH_W) 'watch.c'; else $(CYGPATH_W) '$(srcdir)/watch.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-watch.Tpo" "$(DEPDIR)/sysprof-watch.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-watch.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='watch.c' object='sysprof-watch.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-watch.Po' tmpdepfile='$(DEPDIR)/sysprof-watch.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-watch.obj `if test -f 'watch.c'; then $(CYGPATH_W) 'watch.c'; else $(CYGPATH_W) '$(srcdir)/watch.c'; fi`
+
+sysprof-footreestore.o: footreestore.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-footreestore.o -MD -MP -MF "$(DEPDIR)/sysprof-footreestore.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-footreestore.o `test -f 'footreestore.c' || echo '$(srcdir)/'`footreestore.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-footreestore.Tpo" "$(DEPDIR)/sysprof-footreestore.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-footreestore.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='footreestore.c' object='sysprof-footreestore.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-footreestore.Po' tmpdepfile='$(DEPDIR)/sysprof-footreestore.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-footreestore.o `test -f 'footreestore.c' || echo '$(srcdir)/'`footreestore.c
+
+sysprof-footreestore.obj: footreestore.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-footreestore.obj -MD -MP -MF "$(DEPDIR)/sysprof-footreestore.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-footreestore.obj `if test -f 'footreestore.c'; then $(CYGPATH_W) 'footreestore.c'; else $(CYGPATH_W) '$(srcdir)/footreestore.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-footreestore.Tpo" "$(DEPDIR)/sysprof-footreestore.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-footreestore.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='footreestore.c' object='sysprof-footreestore.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-footreestore.Po' tmpdepfile='$(DEPDIR)/sysprof-footreestore.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-footreestore.obj `if test -f 'footreestore.c'; then $(CYGPATH_W) 'footreestore.c'; else $(CYGPATH_W) '$(srcdir)/footreestore.c'; fi`
+
+sysprof-footreedatalist.o: footreedatalist.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-footreedatalist.o -MD -MP -MF "$(DEPDIR)/sysprof-footreedatalist.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-footreedatalist.o `test -f 'footreedatalist.c' || echo '$(srcdir)/'`footreedatalist.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-footreedatalist.Tpo" "$(DEPDIR)/sysprof-footreedatalist.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-footreedatalist.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='footreedatalist.c' object='sysprof-footreedatalist.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-footreedatalist.Po' tmpdepfile='$(DEPDIR)/sysprof-footreedatalist.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-footreedatalist.o `test -f 'footreedatalist.c' || echo '$(srcdir)/'`footreedatalist.c
+
+sysprof-footreedatalist.obj: footreedatalist.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-footreedatalist.obj -MD -MP -MF "$(DEPDIR)/sysprof-footreedatalist.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-footreedatalist.obj `if test -f 'footreedatalist.c'; then $(CYGPATH_W) 'footreedatalist.c'; else $(CYGPATH_W) '$(srcdir)/footreedatalist.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-footreedatalist.Tpo" "$(DEPDIR)/sysprof-footreedatalist.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-footreedatalist.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='footreedatalist.c' object='sysprof-footreedatalist.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-footreedatalist.Po' tmpdepfile='$(DEPDIR)/sysprof-footreedatalist.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-footreedatalist.obj `if test -f 'footreedatalist.c'; then $(CYGPATH_W) 'footreedatalist.c'; else $(CYGPATH_W) '$(srcdir)/footreedatalist.c'; fi`
+
+sysprof-treeviewutils.o: treeviewutils.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-treeviewutils.o -MD -MP -MF "$(DEPDIR)/sysprof-treeviewutils.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-treeviewutils.o `test -f 'treeviewutils.c' || echo '$(srcdir)/'`treeviewutils.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-treeviewutils.Tpo" "$(DEPDIR)/sysprof-treeviewutils.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-treeviewutils.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='treeviewutils.c' object='sysprof-treeviewutils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-treeviewutils.Po' tmpdepfile='$(DEPDIR)/sysprof-treeviewutils.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-treeviewutils.o `test -f 'treeviewutils.c' || echo '$(srcdir)/'`treeviewutils.c
+
+sysprof-treeviewutils.obj: treeviewutils.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-treeviewutils.obj -MD -MP -MF "$(DEPDIR)/sysprof-treeviewutils.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-treeviewutils.obj `if test -f 'treeviewutils.c'; then $(CYGPATH_W) 'treeviewutils.c'; else $(CYGPATH_W) '$(srcdir)/treeviewutils.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-treeviewutils.Tpo" "$(DEPDIR)/sysprof-treeviewutils.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-treeviewutils.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='treeviewutils.c' object='sysprof-treeviewutils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-treeviewutils.Po' tmpdepfile='$(DEPDIR)/sysprof-treeviewutils.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-treeviewutils.obj `if test -f 'treeviewutils.c'; then $(CYGPATH_W) 'treeviewutils.c'; else $(CYGPATH_W) '$(srcdir)/treeviewutils.c'; fi`
+
+sysprof-sysprof.o: sysprof.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-sysprof.o -MD -MP -MF "$(DEPDIR)/sysprof-sysprof.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-sysprof.o `test -f 'sysprof.c' || echo '$(srcdir)/'`sysprof.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-sysprof.Tpo" "$(DEPDIR)/sysprof-sysprof.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-sysprof.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysprof.c' object='sysprof-sysprof.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-sysprof.Po' tmpdepfile='$(DEPDIR)/sysprof-sysprof.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-sysprof.o `test -f 'sysprof.c' || echo '$(srcdir)/'`sysprof.c
+
+sysprof-sysprof.obj: sysprof.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof-sysprof.obj -MD -MP -MF "$(DEPDIR)/sysprof-sysprof.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof-sysprof.obj `if test -f 'sysprof.c'; then $(CYGPATH_W) 'sysprof.c'; else $(CYGPATH_W) '$(srcdir)/sysprof.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof-sysprof.Tpo" "$(DEPDIR)/sysprof-sysprof.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof-sysprof.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysprof.c' object='sysprof-sysprof.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof-sysprof.Po' tmpdepfile='$(DEPDIR)/sysprof-sysprof.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof-sysprof.obj `if test -f 'sysprof.c'; then $(CYGPATH_W) 'sysprof.c'; else $(CYGPATH_W) '$(srcdir)/sysprof.c'; fi`
+
+sysprof_cli-binfile.o: binfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-binfile.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-binfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-binfile.o `test -f 'binfile.c' || echo '$(srcdir)/'`binfile.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-binfile.Tpo" "$(DEPDIR)/sysprof_cli-binfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-binfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='binfile.c' object='sysprof_cli-binfile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-binfile.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-binfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-binfile.o `test -f 'binfile.c' || echo '$(srcdir)/'`binfile.c
+
+sysprof_cli-binfile.obj: binfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-binfile.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-binfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-binfile.obj `if test -f 'binfile.c'; then $(CYGPATH_W) 'binfile.c'; else $(CYGPATH_W) '$(srcdir)/binfile.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-binfile.Tpo" "$(DEPDIR)/sysprof_cli-binfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-binfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='binfile.c' object='sysprof_cli-binfile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-binfile.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-binfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-binfile.obj `if test -f 'binfile.c'; then $(CYGPATH_W) 'binfile.c'; else $(CYGPATH_W) '$(srcdir)/binfile.c'; fi`
+
+sysprof_cli-collector.o: collector.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-collector.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-collector.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-collector.o `test -f 'collector.c' || echo '$(srcdir)/'`collector.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-collector.Tpo" "$(DEPDIR)/sysprof_cli-collector.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-collector.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='collector.c' object='sysprof_cli-collector.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-collector.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-collector.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-collector.o `test -f 'collector.c' || echo '$(srcdir)/'`collector.c
+
+sysprof_cli-collector.obj: collector.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-collector.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-collector.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-collector.obj `if test -f 'collector.c'; then $(CYGPATH_W) 'collector.c'; else $(CYGPATH_W) '$(srcdir)/collector.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-collector.Tpo" "$(DEPDIR)/sysprof_cli-collector.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-collector.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='collector.c' object='sysprof_cli-collector.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-collector.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-collector.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-collector.obj `if test -f 'collector.c'; then $(CYGPATH_W) 'collector.c'; else $(CYGPATH_W) '$(srcdir)/collector.c'; fi`
+
+sysprof_cli-demangle.o: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-demangle.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-demangle.Tpo" "$(DEPDIR)/sysprof_cli-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='sysprof_cli-demangle.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-demangle.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c
+
+sysprof_cli-demangle.obj: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-demangle.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-demangle.Tpo" "$(DEPDIR)/sysprof_cli-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='sysprof_cli-demangle.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-demangle.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`
+
+sysprof_cli-elfparser.o: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-elfparser.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-elfparser.Tpo" "$(DEPDIR)/sysprof_cli-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='sysprof_cli-elfparser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-elfparser.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c
+
+sysprof_cli-elfparser.obj: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-elfparser.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-elfparser.Tpo" "$(DEPDIR)/sysprof_cli-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='sysprof_cli-elfparser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-elfparser.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`
+
+sysprof_cli-profile.o: profile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-profile.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-profile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-profile.o `test -f 'profile.c' || echo '$(srcdir)/'`profile.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-profile.Tpo" "$(DEPDIR)/sysprof_cli-profile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-profile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='profile.c' object='sysprof_cli-profile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-profile.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-profile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-profile.o `test -f 'profile.c' || echo '$(srcdir)/'`profile.c
+
+sysprof_cli-profile.obj: profile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-profile.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-profile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-profile.obj `if test -f 'profile.c'; then $(CYGPATH_W) 'profile.c'; else $(CYGPATH_W) '$(srcdir)/profile.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-profile.Tpo" "$(DEPDIR)/sysprof_cli-profile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-profile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='profile.c' object='sysprof_cli-profile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-profile.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-profile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-profile.obj `if test -f 'profile.c'; then $(CYGPATH_W) 'profile.c'; else $(CYGPATH_W) '$(srcdir)/profile.c'; fi`
+
+sysprof_cli-sfile.o: sfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-sfile.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-sfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-sfile.o `test -f 'sfile.c' || echo '$(srcdir)/'`sfile.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-sfile.Tpo" "$(DEPDIR)/sysprof_cli-sfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-sfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sfile.c' object='sysprof_cli-sfile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-sfile.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-sfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-sfile.o `test -f 'sfile.c' || echo '$(srcdir)/'`sfile.c
+
+sysprof_cli-sfile.obj: sfile.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-sfile.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-sfile.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-sfile.obj `if test -f 'sfile.c'; then $(CYGPATH_W) 'sfile.c'; else $(CYGPATH_W) '$(srcdir)/sfile.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-sfile.Tpo" "$(DEPDIR)/sysprof_cli-sfile.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-sfile.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sfile.c' object='sysprof_cli-sfile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-sfile.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-sfile.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-sfile.obj `if test -f 'sfile.c'; then $(CYGPATH_W) 'sfile.c'; else $(CYGPATH_W) '$(srcdir)/sfile.c'; fi`
+
+sysprof_cli-sformat.o: sformat.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-sformat.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-sformat.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-sformat.o `test -f 'sformat.c' || echo '$(srcdir)/'`sformat.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-sformat.Tpo" "$(DEPDIR)/sysprof_cli-sformat.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-sformat.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sformat.c' object='sysprof_cli-sformat.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-sformat.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-sformat.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-sformat.o `test -f 'sformat.c' || echo '$(srcdir)/'`sformat.c
+
+sysprof_cli-sformat.obj: sformat.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-sformat.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-sformat.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-sformat.obj `if test -f 'sformat.c'; then $(CYGPATH_W) 'sformat.c'; else $(CYGPATH_W) '$(srcdir)/sformat.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-sformat.Tpo" "$(DEPDIR)/sysprof_cli-sformat.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-sformat.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sformat.c' object='sysprof_cli-sformat.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-sformat.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-sformat.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-sformat.obj `if test -f 'sformat.c'; then $(CYGPATH_W) 'sformat.c'; else $(CYGPATH_W) '$(srcdir)/sformat.c'; fi`
+
+sysprof_cli-stackstash.o: stackstash.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-stackstash.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-stackstash.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-stackstash.o `test -f 'stackstash.c' || echo '$(srcdir)/'`stackstash.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-stackstash.Tpo" "$(DEPDIR)/sysprof_cli-stackstash.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-stackstash.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stackstash.c' object='sysprof_cli-stackstash.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-stackstash.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-stackstash.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-stackstash.o `test -f 'stackstash.c' || echo '$(srcdir)/'`stackstash.c
+
+sysprof_cli-stackstash.obj: stackstash.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-stackstash.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-stackstash.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-stackstash.obj `if test -f 'stackstash.c'; then $(CYGPATH_W) 'stackstash.c'; else $(CYGPATH_W) '$(srcdir)/stackstash.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-stackstash.Tpo" "$(DEPDIR)/sysprof_cli-stackstash.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-stackstash.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stackstash.c' object='sysprof_cli-stackstash.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-stackstash.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-stackstash.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-stackstash.obj `if test -f 'stackstash.c'; then $(CYGPATH_W) 'stackstash.c'; else $(CYGPATH_W) '$(srcdir)/stackstash.c'; fi`
+
+sysprof_cli-tracker.o: tracker.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-tracker.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-tracker.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-tracker.o `test -f 'tracker.c' || echo '$(srcdir)/'`tracker.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-tracker.Tpo" "$(DEPDIR)/sysprof_cli-tracker.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-tracker.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tracker.c' object='sysprof_cli-tracker.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-tracker.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-tracker.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-tracker.o `test -f 'tracker.c' || echo '$(srcdir)/'`tracker.c
+
+sysprof_cli-tracker.obj: tracker.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-tracker.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-tracker.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-tracker.obj `if test -f 'tracker.c'; then $(CYGPATH_W) 'tracker.c'; else $(CYGPATH_W) '$(srcdir)/tracker.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-tracker.Tpo" "$(DEPDIR)/sysprof_cli-tracker.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-tracker.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tracker.c' object='sysprof_cli-tracker.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-tracker.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-tracker.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-tracker.obj `if test -f 'tracker.c'; then $(CYGPATH_W) 'tracker.c'; else $(CYGPATH_W) '$(srcdir)/tracker.c'; fi`
+
+sysprof_cli-unwind.o: unwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-unwind.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-unwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-unwind.o `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-unwind.Tpo" "$(DEPDIR)/sysprof_cli-unwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-unwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unwind.c' object='sysprof_cli-unwind.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-unwind.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-unwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-unwind.o `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c
+
+sysprof_cli-unwind.obj: unwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-unwind.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-unwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-unwind.obj `if test -f 'unwind.c'; then $(CYGPATH_W) 'unwind.c'; else $(CYGPATH_W) '$(srcdir)/unwind.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-unwind.Tpo" "$(DEPDIR)/sysprof_cli-unwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-unwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unwind.c' object='sysprof_cli-unwind.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-unwind.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-unwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-unwind.obj `if test -f 'unwind.c'; then $(CYGPATH_W) 'unwind.c'; else $(CYGPATH_W) '$(srcdir)/unwind.c'; fi`
+
+sysprof_cli-watch.o: watch.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-watch.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-watch.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-watch.o `test -f 'watch.c' || echo '$(srcdir)/'`watch.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-watch.Tpo" "$(DEPDIR)/sysprof_cli-watch.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-watch.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='watch.c' object='sysprof_cli-watch.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-watch.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-watch.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-watch.o `test -f 'watch.c' || echo '$(srcdir)/'`watch.c
+
+sysprof_cli-watch.obj: watch.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-watch.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-watch.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-watch.obj `if test -f 'watch.c'; then $(CYGPATH_W) 'watch.c'; else $(CYGPATH_W) '$(srcdir)/watch.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-watch.Tpo" "$(DEPDIR)/sysprof_cli-watch.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-watch.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='watch.c' object='sysprof_cli-watch.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-watch.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-watch.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-watch.obj `if test -f 'watch.c'; then $(CYGPATH_W) 'watch.c'; else $(CYGPATH_W) '$(srcdir)/watch.c'; fi`
+
+sysprof_cli-signal-handler.o: signal-handler.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-signal-handler.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-signal-handler.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-signal-handler.o `test -f 'signal-handler.c' || echo '$(srcdir)/'`signal-handler.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-signal-handler.Tpo" "$(DEPDIR)/sysprof_cli-signal-handler.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-signal-handler.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='signal-handler.c' object='sysprof_cli-signal-handler.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-signal-handler.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-signal-handler.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-signal-handler.o `test -f 'signal-handler.c' || echo '$(srcdir)/'`signal-handler.c
+
+sysprof_cli-signal-handler.obj: signal-handler.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-signal-handler.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-signal-handler.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-signal-handler.obj `if test -f 'signal-handler.c'; then $(CYGPATH_W) 'signal-handler.c'; else $(CYGPATH_W) '$(srcdir)/signal-handler.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-signal-handler.Tpo" "$(DEPDIR)/sysprof_cli-signal-handler.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-signal-handler.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='signal-handler.c' object='sysprof_cli-signal-handler.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-signal-handler.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-signal-handler.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-signal-handler.obj `if test -f 'signal-handler.c'; then $(CYGPATH_W) 'signal-handler.c'; else $(CYGPATH_W) '$(srcdir)/signal-handler.c'; fi`
+
+sysprof_cli-sysprof-cli.o: sysprof-cli.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-sysprof-cli.o -MD -MP -MF "$(DEPDIR)/sysprof_cli-sysprof-cli.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-sysprof-cli.o `test -f 'sysprof-cli.c' || echo '$(srcdir)/'`sysprof-cli.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-sysprof-cli.Tpo" "$(DEPDIR)/sysprof_cli-sysprof-cli.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-sysprof-cli.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysprof-cli.c' object='sysprof_cli-sysprof-cli.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-sysprof-cli.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-sysprof-cli.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-sysprof-cli.o `test -f 'sysprof-cli.c' || echo '$(srcdir)/'`sysprof-cli.c
+
+sysprof_cli-sysprof-cli.obj: sysprof-cli.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sysprof_cli-sysprof-cli.obj -MD -MP -MF "$(DEPDIR)/sysprof_cli-sysprof-cli.Tpo" \
+@am__fastdepCC_TRUE@ -c -o sysprof_cli-sysprof-cli.obj `if test -f 'sysprof-cli.c'; then $(CYGPATH_W) 'sysprof-cli.c'; else $(CYGPATH_W) '$(srcdir)/sysprof-cli.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/sysprof_cli-sysprof-cli.Tpo" "$(DEPDIR)/sysprof_cli-sysprof-cli.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/sysprof_cli-sysprof-cli.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sysprof-cli.c' object='sysprof_cli-sysprof-cli.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/sysprof_cli-sysprof-cli.Po' tmpdepfile='$(DEPDIR)/sysprof_cli-sysprof-cli.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sysprof_cli_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sysprof_cli-sysprof-cli.obj `if test -f 'sysprof-cli.c'; then $(CYGPATH_W) 'sysprof-cli.c'; else $(CYGPATH_W) '$(srcdir)/sysprof-cli.c'; fi`
+
+testelf-testelf.o: testelf.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testelf-testelf.o -MD -MP -MF "$(DEPDIR)/testelf-testelf.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testelf-testelf.o `test -f 'testelf.c' || echo '$(srcdir)/'`testelf.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testelf-testelf.Tpo" "$(DEPDIR)/testelf-testelf.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testelf-testelf.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='testelf.c' object='testelf-testelf.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testelf-testelf.Po' tmpdepfile='$(DEPDIR)/testelf-testelf.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testelf-testelf.o `test -f 'testelf.c' || echo '$(srcdir)/'`testelf.c
+
+testelf-testelf.obj: testelf.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testelf-testelf.obj -MD -MP -MF "$(DEPDIR)/testelf-testelf.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testelf-testelf.obj `if test -f 'testelf.c'; then $(CYGPATH_W) 'testelf.c'; else $(CYGPATH_W) '$(srcdir)/testelf.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testelf-testelf.Tpo" "$(DEPDIR)/testelf-testelf.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testelf-testelf.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='testelf.c' object='testelf-testelf.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testelf-testelf.Po' tmpdepfile='$(DEPDIR)/testelf-testelf.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testelf-testelf.obj `if test -f 'testelf.c'; then $(CYGPATH_W) 'testelf.c'; else $(CYGPATH_W) '$(srcdir)/testelf.c'; fi`
+
+testelf-demangle.o: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testelf-demangle.o -MD -MP -MF "$(DEPDIR)/testelf-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testelf-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testelf-demangle.Tpo" "$(DEPDIR)/testelf-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testelf-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='testelf-demangle.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testelf-demangle.Po' tmpdepfile='$(DEPDIR)/testelf-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testelf-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c
+
+testelf-demangle.obj: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testelf-demangle.obj -MD -MP -MF "$(DEPDIR)/testelf-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testelf-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testelf-demangle.Tpo" "$(DEPDIR)/testelf-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testelf-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='testelf-demangle.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testelf-demangle.Po' tmpdepfile='$(DEPDIR)/testelf-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testelf-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`
+
+testelf-elfparser.o: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testelf-elfparser.o -MD -MP -MF "$(DEPDIR)/testelf-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testelf-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testelf-elfparser.Tpo" "$(DEPDIR)/testelf-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testelf-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='testelf-elfparser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testelf-elfparser.Po' tmpdepfile='$(DEPDIR)/testelf-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testelf-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c
+
+testelf-elfparser.obj: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testelf-elfparser.obj -MD -MP -MF "$(DEPDIR)/testelf-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testelf-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testelf-elfparser.Tpo" "$(DEPDIR)/testelf-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testelf-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='testelf-elfparser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testelf-elfparser.Po' tmpdepfile='$(DEPDIR)/testelf-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testelf_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testelf-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`
+
+testunwind-testunwind.o: testunwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-testunwind.o -MD -MP -MF "$(DEPDIR)/testunwind-testunwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-testunwind.o `test -f 'testunwind.c' || echo '$(srcdir)/'`testunwind.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-testunwind.Tpo" "$(DEPDIR)/testunwind-testunwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-testunwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='testunwind.c' object='testunwind-testunwind.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-testunwind.Po' tmpdepfile='$(DEPDIR)/testunwind-testunwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-testunwind.o `test -f 'testunwind.c' || echo '$(srcdir)/'`testunwind.c
+
+testunwind-testunwind.obj: testunwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-testunwind.obj -MD -MP -MF "$(DEPDIR)/testunwind-testunwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-testunwind.obj `if test -f 'testunwind.c'; then $(CYGPATH_W) 'testunwind.c'; else $(CYGPATH_W) '$(srcdir)/testunwind.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-testunwind.Tpo" "$(DEPDIR)/testunwind-testunwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-testunwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='testunwind.c' object='testunwind-testunwind.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-testunwind.Po' tmpdepfile='$(DEPDIR)/testunwind-testunwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-testunwind.obj `if test -f 'testunwind.c'; then $(CYGPATH_W) 'testunwind.c'; else $(CYGPATH_W) '$(srcdir)/testunwind.c'; fi`
+
+testunwind-demangle.o: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-demangle.o -MD -MP -MF "$(DEPDIR)/testunwind-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-demangle.Tpo" "$(DEPDIR)/testunwind-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='testunwind-demangle.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-demangle.Po' tmpdepfile='$(DEPDIR)/testunwind-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-demangle.o `test -f 'demangle.c' || echo '$(srcdir)/'`demangle.c
+
+testunwind-demangle.obj: demangle.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-demangle.obj -MD -MP -MF "$(DEPDIR)/testunwind-demangle.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-demangle.Tpo" "$(DEPDIR)/testunwind-demangle.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-demangle.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='demangle.c' object='testunwind-demangle.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-demangle.Po' tmpdepfile='$(DEPDIR)/testunwind-demangle.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-demangle.obj `if test -f 'demangle.c'; then $(CYGPATH_W) 'demangle.c'; else $(CYGPATH_W) '$(srcdir)/demangle.c'; fi`
+
+testunwind-elfparser.o: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-elfparser.o -MD -MP -MF "$(DEPDIR)/testunwind-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-elfparser.Tpo" "$(DEPDIR)/testunwind-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='testunwind-elfparser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-elfparser.Po' tmpdepfile='$(DEPDIR)/testunwind-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-elfparser.o `test -f 'elfparser.c' || echo '$(srcdir)/'`elfparser.c
+
+testunwind-elfparser.obj: elfparser.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-elfparser.obj -MD -MP -MF "$(DEPDIR)/testunwind-elfparser.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-elfparser.Tpo" "$(DEPDIR)/testunwind-elfparser.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-elfparser.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='elfparser.c' object='testunwind-elfparser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-elfparser.Po' tmpdepfile='$(DEPDIR)/testunwind-elfparser.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-elfparser.obj `if test -f 'elfparser.c'; then $(CYGPATH_W) 'elfparser.c'; else $(CYGPATH_W) '$(srcdir)/elfparser.c'; fi`
+
+testunwind-unwind.o: unwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-unwind.o -MD -MP -MF "$(DEPDIR)/testunwind-unwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-unwind.o `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-unwind.Tpo" "$(DEPDIR)/testunwind-unwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-unwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unwind.c' object='testunwind-unwind.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-unwind.Po' tmpdepfile='$(DEPDIR)/testunwind-unwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-unwind.o `test -f 'unwind.c' || echo '$(srcdir)/'`unwind.c
+
+testunwind-unwind.obj: unwind.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testunwind-unwind.obj -MD -MP -MF "$(DEPDIR)/testunwind-unwind.Tpo" \
+@am__fastdepCC_TRUE@ -c -o testunwind-unwind.obj `if test -f 'unwind.c'; then $(CYGPATH_W) 'unwind.c'; else $(CYGPATH_W) '$(srcdir)/unwind.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/testunwind-unwind.Tpo" "$(DEPDIR)/testunwind-unwind.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/testunwind-unwind.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unwind.c' object='testunwind-unwind.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/testunwind-unwind.Po' tmpdepfile='$(DEPDIR)/testunwind-unwind.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testunwind_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testunwind-unwind.obj `if test -f 'unwind.c'; then $(CYGPATH_W) 'unwind.c'; else $(CYGPATH_W) '$(srcdir)/unwind.c'; fi`
uninstall-info-am:
-pixmapsDATA_INSTALL = $(INSTALL_DATA)
-install-pixmapsDATA: $(pixmaps_DATA)
+dist_pixmapsDATA_INSTALL = $(INSTALL_DATA)
+install-dist_pixmapsDATA: $(dist_pixmaps_DATA)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(pixmapsdir)
- @list='$(pixmaps_DATA)'; for p in $$list; do \
+ @list='$(dist_pixmaps_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
- echo " $(pixmapsDATA_INSTALL) $$d$$p $(DESTDIR)$(pixmapsdir)/$$f"; \
- $(pixmapsDATA_INSTALL) $$d$$p $(DESTDIR)$(pixmapsdir)/$$f; \
+ echo " $(dist_pixmapsDATA_INSTALL) $$d$$p $(DESTDIR)$(pixmapsdir)/$$f"; \
+ $(dist_pixmapsDATA_INSTALL) $$d$$p $(DESTDIR)$(pixmapsdir)/$$f; \
done
-uninstall-pixmapsDATA:
+uninstall-dist_pixmapsDATA:
@$(NORMAL_UNINSTALL)
- @list='$(pixmaps_DATA)'; for p in $$list; do \
+ @list='$(dist_pixmaps_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
echo " rm -f $(DESTDIR)$(pixmapsdir)/$$f"; \
rm -f $(DESTDIR)$(pixmapsdir)/$$f; \
done
-pkgdataDATA_INSTALL = $(INSTALL_DATA)
-install-pkgdataDATA: $(pkgdata_DATA)
+dist_pkgdataDATA_INSTALL = $(INSTALL_DATA)
+install-dist_pkgdataDATA: $(dist_pkgdata_DATA)
@$(NORMAL_INSTALL)
$(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
- @list='$(pkgdata_DATA)'; for p in $$list; do \
+ @list='$(dist_pkgdata_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f="`echo $$p | sed -e 's|^.*/||'`"; \
- echo " $(pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \
- $(pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \
+ echo " $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \
+ $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \
done
-uninstall-pkgdataDATA:
+uninstall-dist_pkgdataDATA:
@$(NORMAL_UNINSTALL)
- @list='$(pkgdata_DATA)'; for p in $$list; do \
+ @list='$(dist_pkgdata_DATA)'; for p in $$list; do \
f="`echo $$p | sed -e 's|^.*/||'`"; \
echo " rm -f $(DESTDIR)$(pkgdatadir)/$$f"; \
rm -f $(DESTDIR)$(pkgdatadir)/$$f; \
done
-
-# This directory's subdirectories are mostly independent; you can cd
-# into them and run `make' without going through this Makefile.
-# To change the values of `make' variables: instead of editing Makefiles,
-# (1) if the variable is set in `config.status', edit `config.status'
-# (which will cause the Makefiles to be regenerated when you run `make');
-# (2) otherwise, pass the desired values on the `make' command line.
-$(RECURSIVE_TARGETS):
- @set fnord $$MAKEFLAGS; amf=$$2; \
- dot_seen=no; \
- target=`echo $@ | sed s/-recursive//`; \
- list='$(SUBDIRS)'; for subdir in $$list; do \
- echo "Making $$target in $$subdir"; \
- if test "$$subdir" = "."; then \
- dot_seen=yes; \
- local_target="$$target-am"; \
- else \
- local_target="$$target"; \
- fi; \
- (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
- done; \
- if test "$$dot_seen" = "no"; then \
- $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
- fi; test -z "$$fail"
-
-mostlyclean-recursive clean-recursive distclean-recursive \
-maintainer-clean-recursive:
- @set fnord $$MAKEFLAGS; amf=$$2; \
- dot_seen=no; \
- case "$@" in \
- distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
- *) list='$(SUBDIRS)' ;; \
- esac; \
- rev=''; for subdir in $$list; do \
- if test "$$subdir" = "."; then :; else \
- rev="$$subdir $$rev"; \
- fi; \
- done; \
- rev="$$rev ."; \
- target=`echo $@ | sed s/-recursive//`; \
- for subdir in $$rev; do \
- echo "Making $$target in $$subdir"; \
- if test "$$subdir" = "."; then \
- local_target="$$target-am"; \
- else \
- local_target="$$target"; \
- fi; \
- (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
- done && test -z "$$fail"
-tags-recursive:
- list='$(SUBDIRS)'; for subdir in $$list; do \
- test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+dist_udevDATA_INSTALL = $(INSTALL_DATA)
+install-dist_udevDATA: $(dist_udev_DATA)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(udevdir)
+ @list='$(dist_udev_DATA)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " $(dist_udevDATA_INSTALL) $$d$$p $(DESTDIR)$(udevdir)/$$f"; \
+ $(dist_udevDATA_INSTALL) $$d$$p $(DESTDIR)$(udevdir)/$$f; \
done
-ctags-recursive:
- list='$(SUBDIRS)'; for subdir in $$list; do \
- test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+
+uninstall-dist_udevDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_udev_DATA)'; for p in $$list; do \
+ f="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " rm -f $(DESTDIR)$(udevdir)/$$f"; \
+ rm -f $(DESTDIR)$(udevdir)/$$f; \
done
ETAGS = etags
@@ -414,21 +1331,10 @@ ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
END { for (i in files) print i; }'`; \
mkid -fID $$unique
-TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
- if (etags --etags-include --version) >/dev/null 2>&1; then \
- include_option=--etags-include; \
- else \
- include_option=--include; \
- fi; \
- list='$(SUBDIRS)'; for subdir in $$list; do \
- if test "$$subdir" = .; then :; else \
- test -f $$subdir/TAGS && \
- tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
- fi; \
- done; \
list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
@@ -440,7 +1346,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
$$tags $$unique
ctags: CTAGS
-CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
@@ -478,7 +1384,6 @@ distcleancheck_listfiles = find . -type f -print
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
- $(mkinstalldirs) $(distdir)/module
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
@@ -505,19 +1410,6 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
- list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
- if test "$$subdir" = .; then :; else \
- test -d $(distdir)/$$subdir \
- || mkdir $(distdir)/$$subdir \
- || exit 1; \
- (cd $$subdir && \
- $(MAKE) $(AM_MAKEFLAGS) \
- top_distdir="$(top_distdir)" \
- distdir=../$(distdir)/$$subdir \
- distdir) \
- || exit 1; \
- fi; \
- done
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
@@ -588,21 +1480,20 @@ distcleancheck: distclean
$(distcleancheck_listfiles) ; \
exit 1; } >&2
check-am: all-am
-check: check-recursive
+check: check-am
all-am: Makefile $(PROGRAMS) $(DATA) config.h
-installdirs: installdirs-recursive
-installdirs-am:
- $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(pixmapsdir) $(DESTDIR)$(pkgdatadir)
-install: install-recursive
-install-exec: install-exec-recursive
-install-data: install-data-recursive
-uninstall: uninstall-recursive
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(pixmapsdir) $(DESTDIR)$(pkgdatadir) $(DESTDIR)$(udevdir)
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-installcheck: installcheck-recursive
+installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
@@ -618,83 +1509,76 @@ distclean-generic:
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-clean: clean-recursive
+clean: clean-am
-clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
+ mostlyclean-am
-distclean: distclean-recursive
+distclean: distclean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic distclean-hdr \
distclean-tags
-dvi: dvi-recursive
+dvi: dvi-am
dvi-am:
-info: info-recursive
+info: info-am
info-am:
-install-data-am: install-pixmapsDATA install-pkgdataDATA
+install-data-am: install-dist_pixmapsDATA install-dist_pkgdataDATA \
+ install-dist_udevDATA
install-exec-am: install-binPROGRAMS
-install-info: install-info-recursive
+install-info: install-info-am
install-man:
installcheck-am:
-maintainer-clean: maintainer-clean-recursive
+maintainer-clean: maintainer-clean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
-mostlyclean: mostlyclean-recursive
+mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
-pdf: pdf-recursive
+pdf: pdf-am
pdf-am:
-ps: ps-recursive
+ps: ps-am
ps-am:
-uninstall-am: uninstall-binPROGRAMS uninstall-info-am \
- uninstall-pixmapsDATA uninstall-pkgdataDATA
-
-uninstall-info: uninstall-info-recursive
-
-.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am clean \
- clean-binPROGRAMS clean-generic clean-recursive ctags \
- ctags-recursive dist dist-all dist-gzip distcheck distclean \
- distclean-compile distclean-generic distclean-hdr \
- distclean-recursive distclean-tags distcleancheck distdir \
- distuninstallcheck dvi dvi-am dvi-recursive info info-am \
- info-recursive install install-am install-binPROGRAMS \
- install-data install-data-am install-data-recursive \
- install-exec install-exec-am install-exec-recursive \
- install-info install-info-am install-info-recursive install-man \
- install-pixmapsDATA install-pkgdataDATA install-recursive \
- install-strip installcheck installcheck-am installdirs \
- installdirs-am installdirs-recursive maintainer-clean \
- maintainer-clean-generic maintainer-clean-recursive mostlyclean \
- mostlyclean-compile mostlyclean-generic mostlyclean-recursive \
- pdf pdf-am pdf-recursive ps ps-am ps-recursive tags \
- tags-recursive uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-info-am uninstall-info-recursive \
- uninstall-pixmapsDATA uninstall-pkgdataDATA uninstall-recursive
-
-
-insert-module:
- /sbin/modprobe -r sysprof-module
- /sbin/modprobe sysprof-module
+uninstall-am: uninstall-binPROGRAMS uninstall-dist_pixmapsDATA \
+ uninstall-dist_pkgdataDATA uninstall-dist_udevDATA \
+ uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-noinstPROGRAMS ctags dist dist-all \
+ dist-gzip distcheck distclean distclean-compile \
+ distclean-generic distclean-hdr distclean-tags distcleancheck \
+ distdir distuninstallcheck dvi dvi-am info info-am install \
+ install-am install-binPROGRAMS install-data install-data-am \
+ install-dist_pixmapsDATA install-dist_pkgdataDATA \
+ install-dist_udevDATA install-exec install-exec-am install-info \
+ install-info-am install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-dist_pixmapsDATA \
+ uninstall-dist_pkgdataDATA uninstall-dist_udevDATA \
+ uninstall-info-am
+
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
diff --git a/NEWS b/NEWS
index e69de29..b0b7720 100644
--- a/NEWS
+++ b/NEWS
@@ -0,0 +1,7 @@
+- New 'everything' object
+- New commandline version [Lorenzo Colitti]
+- Assign time spent in kernel to the user process responsible
+- New screenshot window
+- Device based on udev [Kristian Hoegsberg]
+- Port to RHEL4 [Bastien Nocera]
+- Performance improvements
diff --git a/README b/README
index 04ef77c..4384844 100644
--- a/README
+++ b/README
@@ -17,15 +17,14 @@ would be appreciated.
-
Requirements:
-- A Linux kernel version 2.6.11 or newer is required.
- Unlike Sysprof 0.9, this version should work fine on SMP systems.
+- A Linux kernel version 2.6.9 or newer, compiled with profiling
+ support, is required.
- GTK+ 2.6.0 or newer is required
-- libglade 2.5.1 is required
+- libglade 2.5.1 or newer is required
Compiling:
@@ -35,28 +34,31 @@ Compiling:
system compiler, but if you have upgraded your kernel it is
possible that the new kernel was compiled with a different compiler
- In that case, "modprobe sysprof-module" will produce this
+ If the module is compiled with a different compiler than the one
+ compiling the kernel, "modprobe sysprof-module" will produce this
error message:
insmod: error inserting './sysprof-module.o': -1 Invalid module format
-
Debugging symbols
-- The programs you want to profile should have debugging symbols, or
- you won't get much usable information. On a Fedora Core system,
- installing the relevant -debuginfo package should be sufficient.
+- The programs and libraries you want to profile should have debugging
+ symbols, or you won't get much usable information. On a Fedora Core system,
+ installing the relevant <package>-debuginfo package should be sufficient.
+ On Ubuntu and Debian, the debug packages are called <package>-dbg.
-
-- X server.
+- X server
The X server as shipped by most distributions uses its own home-rolled
module loading system and Sysprof has no way to deal with that, so if you
- run sysprof with your normal X serverr you won't get any information about
+ run sysprof with your normal X server you won't get any information about
how time is spent inside the X server.
- To fix this you have to compile your own X server:
+ On Ubuntu and Debian there is a package, xserver-xorg-dbg, containing a
+ binary called Xorg-debug that is built in such a way that sysprof can use
+ it. On other systems, to get an X server with usable symbols you
+ have to compile your own:
(1) Compile the X server to use ".so" modules:
@@ -64,17 +66,20 @@ Debugging symbols
xc/config/cf/xorgsite.def.
If you are compiling the CVS version of the X server
- (the one that will eventually become 7.0), then this is
+ (the one that will eventually become 6.9), then this is
already the default.
- "make World"
+
+ - Don't run "make install" yet. (See below).
- (2) Install the X server making sure it can't see any ".a" files. If
- you install on top of an existing installation, just do
+ (2) Make sure the new X server can't see any old ".a" files lying
+ around. If you install on top of an existing installation, just do
find /usr/X11R6/lib/"*.a" | sudo xargs rm
- and install the newly compiled X server.
+ then run "make install" as root to install the newly compiled
+ X server.
If a ".so" X server finds .a files in its module path it will
try to load those in preference to .so files and this causes
@@ -87,13 +92,12 @@ Debugging symbols
you why, but then I'd have to kill you.
-
-
Credits:
+ Lorenzo Colitti for writing the sysprof-text program
Diana Fong for the icon
Mike Frysinger for x86-64 support
Kristian Høgsberg for the first port to the 2.6 kernel.
Owen Taylor for the symbol lookup code in memprof
-
+
Søren (sandmann@daimi.au.dk)
diff --git a/TODO b/TODO
index 3fd3aae..8df0cc2 100644
--- a/TODO
+++ b/TODO
@@ -1,183 +1,636 @@
-Before 1.0:
+Before 1.1:
- - Update version numbers and year in source
- - configure.ac
- - sysprof.c about box
- - any file that changed
+* Move perfcounter.h into sysprof namespace
- - make announce-1.0.11 file
- - search-and-replace 1.0.(x-1) 1.0.x
- - Add notes in sysprof-1.0.x section
+* Check for existence and presense of __NR_perf_counter_open in
+ syscall.h
- - git add announce-1.0.x
+Before 1.2:
- - Make tarball
+* Filter out kernel stacktrace entries from the interrupts.
+ (This can be done better in the kernel).
- - Check that tarball works
+* Make sure errors are reported properly.
- - git commit
+* Fork GtkLabel and change it to only request a resize when the new size is
+ actually larger than the old one. It could also be special-purposed for
+ the "Samples: <number>" format with the number left-aligned.
- - git tag -a sysprof-1.0.11 -m "Sysprof 1.0.11"
+* When should the samples label be updated? On one hand we don't want
+ it to be the only thing that shows up on the profile. On the other,
+ when there are things going on, it should update quickly.
- - Update website
+ It is also desirable that it updates slowly if there is slow
+ activity going on; for example if you are moving the mouse cursor
+ around.
- - Announce on Freshmeat
+ Cost of updating the samples label: s (in samples)
+ Base rate: f
- - Announce on gnome-announce
- - Announce on kernel list.
+ If we update the samples label c times per second, the frequency is
- - Announce on Gnomefiles
+ c * s
+
+ If we update the samples label for every k samples.
- - Announce on news.gnome.org
- - Send to slashdot/developers
- - Announce on devtools list (?)
+ (k - s)/f is the time it takes before updating
- - Announce on Advogato
- link to archive
+ So if the update rate should be proportional to the base rate, then
+ we get
-Before 1.0.1:
+ (k - s)/f = d/f
-* See if we can reproduce the problem where libraries didn't get correctly
- reloaded after new versions were installed.
+ which implies k = d + s. So we should pick some constant d and only
+ update when that many samples have arrived.
-* Build system
- - Find out what distributions it actually works on
- (ask for sucess/failure-stories in 1.0 releases)
+* The counters seem to not be disabled when looking at the
+ profiles.
+* Build system
- Create RPM package? See fedora-packaging-list for information
about how to package kernel modules. Lots of threads in
June 2005 and forward.
See also http://www.fedoraproject.org/wiki/Extras/KernelModuleProposal
-Before 1.2:
-
-* See if the auto-expanding can be made more intelligent
+ Someone already did create a package - should be googlable.
-* Send entire stack to user space, then do stackwalking there. That would
- allow us to do more complex algorithms, like dwarf, in userspace. Though
- we'd lose the ability to do non-racy file naming. We could pass a list
- of the process mappings with each stack though.
+* The hrtimer in the kernel currently generates an event every time
+ the timer fires. There are two problems with this:
-* If interrupt happens in kernel mode, send both
- kernel stack and user space stack, have userspace stitch them
- together. (User space stack must probably be done in a thread - kernel
- stack must probably be taken in the interrupt itself?
- - Why this difference? The page tables should still be loaded. Is it
- because pages_present() doesn't work? No, turning it off doesn't help.
- - It looks like this works. Get:
+ - Essentially all the events are idle events and exclude_idle is
+ ignored.
- struct pt_regs *user_regs =
- (void *)current->thread.esp0 - sizeof (struct pt_regs);
+ - If you make it obey exclude_idle, it still generates activity
+ based on what is running currently. Unfortunately, the thing that
+ is running will very often be the userspace tracker because it was
+ handling the last sample generated. So this degenerates into an
+ infinite loop of sorts. Or maybe the GUI is just too slow, but
+ then why doesn't it happen with the real counters?
- then use pages_present as usual to trace with user_regs; There could be
- rare lockups though.
+ I think the solution here is to make the hrtimer fire at some
+ reasonable interval, like 100000 ns. When the timer fires, if the
+ current task is not the idle taks, it increments a counter by
+
+ cpu clock frequency * 100000 ns
-* Correctness
- - When the module is unloaded, kill all processes blocking in read
- - or block unloading until all processes have exited
- Unfortunately this is basically impossible to do with a /proc
- file (no open() notification). So, for 1.0 this will have to be
- a dont-do-that-then. For 1.2, we should do it with a sysfs and
- kobject instead.
+ If that overflows the sample period, an event is generated.
- - When the module is unloaded, can we somehow *guarantee* that no
- kernel thread is active? Doesn't look like it; however we can
- get close by decreasing a ref count just before returning
- from the module. (There may still be return instructions etc.
- that will get run). This may not be an issue with the timer
- based scanning we are using currently.
+ This is closer to the idea of a fake CPU cycle counter.
-- See if there is a way to make it distcheck
+ Also, for reasons I don't understand, it stops generating events
+ completely if a process is running that spins on all CPUs. So this
+ interface is not usable in its present state, but fortunately all
+ CPUs we care about have hardware cycle counters.
-- grep FIXME - not10
+* With more than one CPU, we can get events out of order, so userspace
+ will have to deal with that. With serial numbers we could do it
+ correctly, but even without them we can do a pretty reasonable job
+ of putting them back in order. If a fork happens "soon" after a
+ sample, it probably happened before the sample; if an mmap happens
+ "soon" after a sample that would otherwise be unmapped, it probably
+ happened before the sample. All we need is a way to determine what
+ "soon" is.
-- translation should be hooked up
+ Serial numbers would be useful to make "soon" an accurate measure.
-- Consider adding "at least 5% inclusive cost" filter
+ There is also the issue of pid reuse, but that can probably be
+ ignored.
-- consider having the ability to group a function together with its nearest
- neighbours. That way we can eliminate some of the effect of
- "one function taking 10% of the time"
- vs.
- "the same function broken into ten functions each taking 1%"
- Not clear what the UI looks like though.
+ If we ignore pid reuse, we can sort the event buffer where two
+ events compare equal, unless both have the same pid and one is a
+ fork and the other is not.
-- Ability to generate "screenshots" suitable for mail/blog/etc
- UI: "generate screenshot" menu item pops up a window with
- a text area + a radio buttons "text/html". When you flick
- them, the text area is automatically updated.
+ A system-wide serial number could be expensive to maintain though,
+ so maybe time events would be just as good.
-- Fixing the oops in kernels < 2.6.11
+* Another issue is processes that exit during the initial scan of
+ /proc. Such a process will not cause sample events by itself, but it
+ may fork a child that will. There is no way to get maps for that
+ child.
- - Probably just require 2.6.11 (necessary for timer interrupt
- based anyway).
+ A possible solution would be to optionally generate mmap event after
+ forks. Userspace would turn this off when it was done with the
+ initial gathering of processes.
- - Make the process waiting in poll() responsible for extracting
- the backtrace. Give a copy of the entire stack rather than doing
- the walk inside the kernel.
+ Also, exec() events will delete the maps from a process, but all we
+ get is 'comm' events which is not quite the same thing.
- New model:
- - Two arrays,
- one of actual scanned stacks
- one of tasks that need to be scanned
- One wait queue,
- wait for data
+* Find out why the busy cursor stays active after you hit start
- - in read() wait for stack data:
- scan_tasks()
- if (!stack_data)
- return -EWOULDBLOCK;
+* Kernel binary when available, is better than kallsyms.
- in poll()
- while (!stack data) {
- wait_for_data();
- scan_tasks();
- }
- return READABLE;
+* GTK+ bugs:
+ - Misposition window after click
+ - Find out why gtk_tree_view_columns_autosize() apparently doesn't
+ work on empty tree views.
+ - Write my own tree model? There is still performance issues in
+ FooTreeStore.
- scan_tasks() is a function that converts waiting
- tasks into data, and wakes them up.
+* Counters must not be destroyed during tracker setup. They have to
+ exist but be disabled so that we can track process creation.
- - in timer interrupt:
+* Check that we don't use too much memory (in particular with the
+ timeline).
- if (someone waiting in poll() &&
- current && current != that_someone &&
- current is runnable)
- {
- stop current;
- add current to queue;
- wake wait_for_data;
- }
+* Fix names. "new process" is really "exec". (What does "comm"
+ actually stand for? Command?)
- This way, we will have a real userspace process
- that can take the page faults.
+* Fix ugly flash when double clicking in descendants view
-
- - Different approach:
+* Find out what's up with weird two-step flash when you hit start when
+ a profile is loaded.
- pollable file where a regular userspace process
- can read a pid. Any pid returned is guaranteed to be
- UNINTERRUPTIBLE. Userspace process is required to
- start it again when it is done with it.
+* Make tracker creation faster. (Faster populating mainly)
- Also provide interface to read arbitrary memory of
- that process.
+* Share map reading code between vdso stuff in binfile.c and tracker.c
- ptrace() could in principle do all this, but
- unfortunately it sucks to continuously
- ptrace() processes.
+* Get rid of remaining gulongs (use uint64_t instead)
- - Yet another
+* Move binfile hash table to state_t.
- Userspace process can register itself as "profiler"
- and pass in a filedescriptor where all sorts of
- information is sent.
+* Get rid of process.c
- - could tie lifetime of module to profiler
- - could send "module going away" information
- - Can we map filedescriptors to files in
- a module?
+* On 32 bit, NMI stackframes are not filtered out which leads to wrong
+ kernel traces
+
+* Can we track kernel modules being inserted/removed?
+
+* Does it make sense to try and locate a kernel binary, or can we
+ always just use kallsyms.
+
+* open_inode() would be useful in many cases
+
+* Is the double mapping of the ring buffer ok?
+
+* Why do we get EINVAL when we try to track forks?
+
+* Sometimes it gets samples for unknown processes. This may be due to
+ forking without execing.
+
+* Give an informative error message if not run as root
+
+* What are the events generated for pid 0 and pid 1? They have no
+ stacktrace, and an eip in the kernel.
+
+* Delete the binparser stuff and fix the elf parser to just deal with
+ 32 bits vs 64 bits. Or use C++ like behdad does in harfbuzz?
+
+* We often get "No map". I suspect this is because the vdso stackframe
+ is strange.
+
+* Hack to disable recursion for binaries without symbols causes the
+ symbols to not work the way other symbols do. A better approach is
+ probably to simply generate a new symbol for every appearance except
+ leaf nodes, which should still be considered one symbol (or maybe be
+ considered the same symbol if they have the same parent). In fact
+ "has same parent" may be the correct criterion in all cases. (done:
+ master now doesn't fold those recursions anymore)
+
+ * See if we can make "In file <blah>" not be treated as a recursive
+ function. Maybe simply treat each individual address in the file
+ as a function. Or try to parse the machine code. Positions that
+ are called are likely to be functions.
+
+ - Treat identical addresses as one function
+
+ - Treat all addresses within a library that don't have children
+ are treated as one function.
+
+ This will have the effect of coalescing adjacent siblings without
+ children. Which is what you want since you can't tell them apart
+ anyway. It will never be a great experience though.
+
+* Make sure that labels look decent in case of "No Map" etc.
+
+* Elf bugs:
+ - error handling for bin_parser is necessary.
+
+ * Find out why all apps have an "In file /usr/bin/<app binary>" below
+ _libc_main. If possible, maybe make up a name for it.
+
+* vdso stuff:
+ - the "[vdso]" string should be #defined somewhere
+ - Does get_vdso_bytes() belong in process.c?
+ - Is basing on "[vdso]" always correct?
+
+* Convert things like [heap] and [stack] to more understandable labels.
+
+* Strategies for taking reliable stacktraces.
+
+ Three different kinds of files
+
+ - kernel
+ - vdso
+ - regular elf files
+
+ - kernel
+ - eh_frame annotations, in kernel or in kernel debug
+ - /proc/kallsyms
+ - userspace can look at _stext and _etext to determine
+ start and end of kernel text segment
+ - copying kernel stack to userspace
+ - it's always 4096 bytes these days
+ - heuristically determine functions based on address
+ - callbacks on the stack can be identified
+ by having an offset of 0.
+ - even so there is a lot of false positives.
+ - is eh_frame usually loaded into memory during normal
+ operation? It is mapped, but probably not paged in,
+ so we will be taking a few major page faults when we
+ first profile something.
+ Unless of course, we store the entire stack in
+ the stackstash. This may use way too much memory though.
+
+ - Locking, possibly useful code:
+
+ /* In principle we should use get_task_mm() but
+ * that will use task_lock() leading to deadlock
+ * if somebody already has the lock
+ */
+ if (spin_is_locked (&current->alloc_lock))
+ printk ("alreadylocked\n");
+ {
+ struct mm_struct *mm = current->mm;
+ if (mm)
+ {
+ printk (KERN_ALERT "stack size: %d (%d)\n",
+ mm->start_stack - regs->REG_STACK_PTR,
+ current->pid);
+
+ stacksize = mm->start_stack - regs->REG_STACK_PTR;
+ }
+ else
+ stacksize = 1;
+ }
+
+ - regular elf
+ - usually have eh_frame section which is mapped into memory
+ during normal operation
+ - do stackwalk in kernel based on eh_frame
+ - eh_frame section is usually mapped into memory, so
+ no file reading in kernel would be necessary.
+ - do stackwalk in userland based on eh_frame
+ - do ebp based stackwalk in kernel
+ - do ebp based stackwalk in userland
+ - do heuristic stackwalk in kernel
+ - do heuristic stackwalk in userland
+
+ - Send heuristic stack trace to user space, along with
+ location on the stack. Then, in userspace analyze the
+ machine code to determine the size of the stack frame at any
+ point. The instructions that would need to be recognized are:
+
+ subl <constant>, %esp
+ addl <constant>, %esp
+ leave
+ jcc
+ push
+ pop
+
+ GCC is unlikely to have different stack sizes at the entry
+ to a basic block.
+
+ We can often find a vmlinux in /lib/modules/<uname-r>/build.
+
+* "Expand all" is horrendously slow because update_screenshot gets called
+ for every "expanded" signal. In fact even normal expanding is really
+ slow. It's probably hopeless to get decent performance out of GtkTreeView,
+ so we will have to store a list of expanded objects and keep that uptodate
+ as the rows expands and unexpands.
+
+* Give more sensible 'error messages'. Eg., if you get permission denied for
+ a file, put "Permission denied" instead of "No map"
+
+* crc32 checking probably doesn't belong in elfparser.c
+
+* Missing things in binparser.[ch]
+
+ - it's inconvenient that you have to pass in both a parser _and_
+ a record. The record should just contain a pointer to the parser.
+ On the other hand, the result does depend on the parser->offset.
+ So it's a bit confusing that it's not passed in.
+
+ - the bin_parser_seek_record (..., 1); idiom is a little dubious
+
+ - Add error checking
+ Also need to add error checking.
+
+ - "native endian" is probably not useful. Maybe go back to just
+ having big/little endian.
+
+ Should probably rethink the whole thing. It's just not very convenient to use, even
+ for simple things like ELF files.
+
+* Rename stack_stash_foreach_by_address() to stack_stash_foreach_unique(),
+ or maybe not ...
+
+ Which things are we actually using from stack stash now?
+
+* Maybe report idle time? Although this would come for free with the
+ timelines.
+
+* Fix (deleted) problem. But more generally, whenever we can't display a
+ symbol, display an error message instead, ie.,
+ - 'Binary file <xxxx> was deleted/replaced'
+ - 'No mapping for address'
+ - 'No symbols in binary file'
+ - 'Address has no corrresponding symbol in file'
+ - etc.
+ done: HEAD will not load files with the wrong inode now.
+
+* Consider whether ProfileDescendant can be done with a StackStash We
+ need the "go-back-on-recursion" behavior. That could be added of
+ course ... the functions are otherwise very similar.
+
+* Add spew infrastructure to make remote debugging easier.
+
+* Make it compile and work on x86-64
+
+- 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.
+ - The unique_dup() scheme is somewhat confusing.
+ - a more pragmatic approach might be to just walk the tree and
+ save it.
+
+- plug all the leaks
+ - loading and saving probably leak right now
+
+- make it use less memory:
+ - StackNodes are dominating
+ - fold 'toplevel' into 'size'
+ - allocate in blocks
+ - this will need to be coordinated with
+ profile.c which also creates stacknodes.
+
+ - maybe simply make stackstashes able to
+ save themselves.
+
+- rethink loading and saving. Goals
+
+ - Can load 1.0 profiles
+ - Don't export too much of stackstashes to the rest of the
+ app
+
+ Features:
+
+ * Have a compatibility module that can load 1.0 modules
+ - reads in the call tree, then generate a stack stash
+ by repeatedly adding traces.
+
+ * Make stackstash able to save themselves using callbacks.
+
+ * If loading a file fails, then try loading it using the
+ 1.0 loader.
+ - optimization: make sure we fail immediately if we
+ see an unknown object.
+
+ * Add versioning support to sfile.[ch]:
+ - sformat_new() should take doctype and version strings:
+ like "sysprof-profile" "version 1.2"
+ - there should be sfile_sniff() functionality that will
+ return the doctype and version of a file. Or None
+ if there aren't any.
+
+ * At this point, make the loader first check if the file has a version
+ if it doesn't, load as 1.0, otherwise as whatever the version claims
+ it is.
+
+ * Make provisions for forward compatibility: maybe it should be
+ possible to load records with more fields than specified.
+
+ * Figure out how to make sfile.[ch] use less memory.
+
+ - In general clean sfile.[ch] up a little:
+
+ - split out dfa in its own generic class
+
+ - make a generic representation of xml files with quarks for strings:
+ struct item {
+ int begin/end/text;
+ quark text: -> begin/end/value
+ int id; -> for begins that are pointed to
+ }
+ perhaps even with iterators. Should be compact and suitable for both
+ input and output. As a first cut, perhaps just split out the
+ Instruction code.
+
+ (done, somewhat).
+
+ - make the api saner; add format/content structs
+ Idea: all types have to be declared:
+
+ SFormat *format = sformat_new()
+ SType *object_list = stype_new (format);
+ SType *object = stype_new (format);
+ ...
+
+ stype_define_list (object_list, "objects", object);
+ stype_define_record (object, "object",
+ name, total, self, NULL);
+ stype_define_pointer (...);
+
+* See if the auto-expanding can be made more intelligent
+ - "Everything" should be expanded exactly one level
+ - all trees should be expanded at least one level
+
+* Send entire stack to user space, then do stackwalking there. That would
+ allow us to do more complex algorithms, like dwarf, in userspace. Though
+ we'd lose the ability to do non-racy file naming. We could pass a list
+ of the process mappings with each stack though. Doing this would also solve
+ the problem of not being able to get maps of processes running as root.
+ Might be too expensive though. User stacks seem to be on the order
+ of 100K usually, which for 200 times a second means a bandwidth of
+ 20MB/s, which is probably too much. One question is how much of it
+ usually changes.
+ Actually it seems that the _interesting_ part of the stack
+ (ie., from the stack pointer and up) is not that big in many cases. The
+ average stacksize seemed to be about 7700 bytes for gcc compiling gtk+.
+ Even deeply recursive apps like sysprof only generate about 55K stacks.
+
+ Other possibilities:
+
+ - Do heuristic stack walking where it lists all words on the stack
+ that look like they might be return addresses.
+
+ - Somehow map the application's stack pages into the client. This
+ is likely difficult or impossible.
+
+ - Another idea: copy all addresses that look like they could be
+ return addresses, along with the location on the stack. This
+ just might be enough for a userspace stack walker.
+
+ - Yet another: krh suggests hashing blocks of the stack, then
+ only sending the blocks that changed since last time.
+
+ - every time you send a stackblock, also send a cookie.
+
+ - whenever you *don't* send a stackblock, send the cookie
+ instead. That way you always get a complete stacktrace
+ conceptually.
+
+ - also, that would allow the kernel to just have a simple
+ hashtable containing the known blocks. Though, that could
+ become large. Actually there is no reason to store the
+ blocks; you can just send the hashcode. That way you
+ would only need to store a list of hashcodes that we
+ have generated previously.
+
+ - One problem with doing DWARF walking is that the debug code
+ will have to be faulted in. This can be a substantial amount
+ of disk access which is undesirable to have during a
+ profiling run. Even if we only have to fault in the
+ .eh_frame_hdr section, that's still 18 pages for gtk+. The
+ .eh_frame section for gtk+ is 72 pages.
+
+ A possibility may be to consider two stacktraces identical
+ if the only differing values are *outside* the text
+ segments. This may work since stack frames tend to be the
+ same size. Is there a way of determining the location of
+ text segments without reading the ELF files? Maybe just
+ check if it's inside an executable mappign.
+
+ It is then sufficient in user space to only store one
+ representative for each set of considered-identical stack
+ traces.
+
+ User space storage: Use the stackstash tree. When a new trace
+ is added, just skip over nodes that differ, but where none of
+ them points to text segments. Two possibilities then:
+
+ - when two traces are determined to differ, store them
+ in completely separate trees. This ensures that we
+ will never run the dwarf algorithm on an invalid
+ stack trace, but also means that we won't get shared
+ prefixes for stacktraces.
+
+ - when two traces are determined to differ, branch off
+ as currently. This will share more data, but the
+ dwarf algorithm could be run on invalid traces. It
+ may work in practice though if the compiler
+ generally uses fixed stack frames.
+
+ A twist on is to mark the complete stack traces as
+ "complete". Then after running the DWARF algorithm,
+ the generated stack trace can be saved with it. This
+ way incomplete stack traces branching off a complete
+ one can be completed using the DWARF information for
+ the shared part.
+
+* Notes on heuristic stack walking
+
+ - We can reject addresses that point exactly to the beginning of a
+ function since these are likely callbacks. Note though that the
+ first time a function in a shared library is called, it goes
+ through dynamic linker resolution which will cause the stack to
+ contain a callback of the function. This needs to be investigated
+ in more detail.
+
+ - We are already rejecting addresses outside the text section
+ (addresses of global variables and the like).
+
+* How to get the user stack:
+
+ /* In principle we should use get_task_mm() but
+ * that will use task_lock() leading to deadlock
+ * if somebody already has the lock
+ */
+ if (spin_is_locked (&current->alloc_lock))
+ printk ("alreadylocked\n");
+ {
+ struct mm_struct *mm = current->mm;
+ if (mm)
+ {
+ printk (KERN_ALERT "stack size: %d (%d)\n",
+ mm->start_stack - regs->REG_STACK_PTR,
+ current->pid);
+
+ stacksize = mm->start_stack - regs->REG_STACK_PTR;
+ }
+ else
+ stacksize = 1;
+ }
+
+* If interrupt happens in kernel mode, send both
+ kernel stack and user space stack, have userspace stitch them
+ together. well, they could be stitched together in the kernel.
+ Already done: we now take a stacktrace of the user space process
+ when the interrupt happens in kernel mode. (Unfortunately, this
+ causes lockups on many kernels (though I haven't seen that for
+ a while)).
+
+ We don't take any stacktraces of the kernel though. Things that
+ need to be
+ investigated:
+ - does the kernel come with dwarf debug information?
+ - does the kernel come with some other debug info
+ - is there a place where the vmlinux binary is usually
+ placed? (We should avoid any "location of vmlinux" type
+ questions if at all possible).
+
+ We do now copy the kernel stack to userspace and do a
+ heuristic stack walk there. It may be better at some point to
+ use dump_trace() in the kernel since that will do some
+ filtering by itself.
+
+ Notes about kernel symbol lookup:
+
+ - /proc/kallsym is there, but it includes things like labels.
+ There is no way to tell them from functions
+
+ - We can search for a kernel binary with symbols. If the
+ kernel-debug package is installed, or if the user compiled
+ his own kernel, we will find one. This is a regular elf file.
+ It also includes labels, but we can tell by the STT_INFO field
+ which is which.
+
+ Note though that for some labels we do actually want to
+ treat them as functions. For example the "page_fault" label,
+ which is function by itself. We can recognize these by the
+ fact that their symbols have a size. However, the _start
+ function in normal applications does not have a size, so the
+ heuristic should be something like this:
+
+ - If the address is inside the range of some symbol, use
+ that symbol
+
+ - Otherwise, if the closest symbol is a function with
+ size 0, use that function.
+
+ This means the datastructure will probably have to be done a
+ little differently.
+
+- See if there is a way to make it distcheck
+
+- grep "FIXME - not10"
+- grep FIXME
+
+- translation should be hooked up
+
+- Consider adding "at least 5% inclusive cost" filter
+
+- consider having the ability to group a function together with its nearest
+ neighbours. That way we can eliminate some of the effect of
+ "one function taking 10% of the time"
+ vs.
+ "the same function broken into ten functions each taking 1%"
+ Not clear what the UI looks like though.
+
+- Ability to generate "screenshots" suitable for mail/blog/etc
+ UI: "generate screenshot" menu item pops up a window with
+ a text area + a radio buttons "text/html". When you flick
+ them, the text area is automatically updated.
+ - beginning in CVS:
+ - why does the window not remember its position when
+ you close it with the close button, but does remember
+ it when you use the wm button or the menu item? It actually
+ seems that it only forgets the position when you click the
+ button with the mouse. But not if you use the keyboard ...
+ This is a gtk+ bug.
- Find out how gdb does backtraces; they may have a better way. Also
find out what dwarf2 is and how to use it. Look into libunwind.
@@ -189,41 +642,17 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html
look in dwarf2-frame.[ch] in the gdb distribution.
+ Also look at bozo-profiler
+ http://cutebugs.net/bozo-profiler/
+ which has an elf32 parser/debugger
+
- Make busy cursors more intelligent
- when you click something in the main list and we don't respond
within 50ms (or perhaps when we expect to not be able to do
so (can we know the size in advance?))
- 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
-
- - profile should take traces of pointers to presentation
- objects without knowing anything about these presentation
- objects.
-
- - Creating a profile is then
-
- - 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
- (probably we get that for free with stackstash reorganisation)
-
-- support more than one reader of the samples properly
- - Don't generate them if noone cares
- - When not profiling, sysprof shouldn't care
-
-- Add ability to show more than one function at a time. Algorithm:
+- Consider adding ability to show more than one function at a time. Algorithm:
Find all relevant nodes;
For each relevant node
best_so_far = relevant node
@@ -235,55 +664,71 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html
list leaves
for each leaf
add trace to tree (leaf, interesting)
+
- Consider adding KDE-style nested callgraph view
- probably need a dependency on gtk+ 2.8 (cairo) for this.
+ - Matthias has code for something like this.
+ - See http://www.marzocca.net/linux/baobab.html
+ - Also see http://www.cs.umd.edu/hcil/treemap-history/index.shtml
+
- Add support for line numbers within functions
- Possibly a special "view details" mode, assuming that
the details of a function are not that interesting
- together with a tree.
-- rethink caller list, not terribly useful at the moment. Federico suggested
- listing all ancestors.
-
-
-- Have kernel module report the file the address was found in
- Should avoid a lot of potential broken/raciness with dlopen etc.
-- Make things faster
- - Can I get it to profile itself?
- - speedprof seems to report that lots of time is spent in
- stack_stash_foreach() and also in generate_key()
-- add an 'everything' object. It is really needed for a lot of things
- - should be easy to do with stackstash reorganization.
+ together with a tree. (Could add radio buttons somewhere in
+ in the right pane). Or tabs.
+ - Open a new window for the function.
-- Non-GUI version that can save in a format the GUI can understand.
- Could be used for profiling startup etc. Would preferably be able to
- dump the data to a network socket. Should be able to react to eg.
- SIGUSR1 by dumping the data.
-
- Work done by Lorenzo:
+- Add view->ancestors/descendants menu items
- http://www.colitti.com/lorenzo/software/gnome-startup/sysprof-text.diff
- http://www.colitti.com/lorenzo/software/gnome-startup/sysprof.log
- http://colitti.com/lorenzo/software/gnome-startup/
+- rethink caller list, not terribly useful at the moment. Federico suggested
+ listing all ancestors.
+ Done: implemented this idea in CVS HEAD. If we keep it that way,
+ should do a globale s/callers/ancestors on the code.
+ - not sure it's an improvement. Often it is more interesting to
+ find the immediate callers.
+ - Now it's back to just listing the immediate callers.
- Figure out how Google's pprof script works. Then add real call graph
drawing. (google's script is really simple; uses dot from graphviz).
+ KCacheGrind also uses dot to do graph drawing.
- hide internal stuff in ProfileDescendant
-- possibly add dependency on glib 2.8 if it is released at that point.
- (g_file_replace())
+Later:
+
+- Multithreading is possible in a number of places.
+
+- If the stack trace ends in a memory access instruction, send the
+ vma information to userspace. Then have user space
+ produce statistics on what types of memory are accessed.
- somehow get access to VSEnterprise profiler and see how it works.
somehow get access to vtune and see how it works.
-Later:
+- On SMP systems interrupts happen unpredictably, including when another
+ one is running. Right now we are ignoring any interrupts that happen
+ when another one is running, but we should probably just save the data
+ in a per-cpu buffer.
+
+- Find out if sysprof accurately reports time spent handling pagefaults.
+ There is evidence that it doesn't:
+ - a version of sysprof took 10 seconds to load a certain profile.
+ Profiling itself it appeared that most of the time was spent
+ in the GMarkup parser
+ - a newer version of sysprof with significantly more compact
+ Instructions structure took about 5 seconds, but the profile
+ looked about the same.
+ The difference between the two versions has to be in page faults/
+ memory speed, but the profiles looked similar.
+ Try and reproduce this in a more controlled experiment.
- See if it is possible to group the X server activity under the process that
generated it.
- .desktop file
[Is this worth it? You will often want to start it as root,
- and you will need to insert the module from the command line]
+ and you will need to insert the module from the comman line]
+
- Applications should be able to say "start profiling", "stop profiling"
so that you can limit the profiling to specific areas.
Idea:
@@ -295,12 +740,8 @@ Later:
- Find out how to hack around gtk+ bug causing multiple double clicks
to get eaten.
-- Consider what it would take to take stacktraces of other languages
-
- - perl,
- - python
- - java
- - bash
+- Consider what it would take to take stacktraces of other languages such
+ as perl, python, java, ruby, or bash. Or scheme.
Possible solution is for the script binaries to have a function
called something like
@@ -312,10 +753,14 @@ Later:
This function would behave essentially like a signal handler: couldn't
call malloc(), couldn't call printf(), etc.
- Note thought that scripting languages will generally have a stack with
+ Note though that scripting languages will generally have a stack with
both script-binary-stack, script stack, and library stacks. We wouldn't
want scripts to need to parse dwarf. Also if we do that thing with
- sending the entire stack to userspace, things will be further complicated.
+ sending the entire stack to userspace, things will be further
+ complicated.
+
+ Also note languages like scheme that uses heap allocated activation
+ records.
- Consider this usecase:
Someone is considering replacing malloc()/free() with a freelist
@@ -386,14 +831,18 @@ Later:
Cookies are used to figure out whether an access is really the same, ie., for two identical
cookies, the size is still just one, however
- Memory is different from disk because you can't reasonably assume that stuff that has
- been read will stay in cache (for short profile runs you can assume that with disk,
- but not for long ones).
+ Memory is different from disk because you can't reasonably assume
+ that stuff that has been read will stay in cache (for short profile
+ runs you can assume that with disk, but not for long ones).
- - Perhaps show a timeline with CPU in one color and disk in one color. Allow people to
- look at at subintervals of this timeline. Is it useful to look at both CPU and disk at
- the same time? Probably not. See also marker discussion above. UI should probably allow
- double clicking on a marked section and all instances of that one would be marked.
+ - Perhaps show a timeline with CPU in one color and disk in one
+ color. Allow people to look at at subintervals of this
+ timeline. Is it useful to look at both CPU and disk at the same
+ time? Probably not. See also marker discussion above. UI should
+ probably allow double clicking on a marked section and all
+ instances of that one would be marked.
+
+ - This also allows us to show how well multicore CPUs are being used.
- Other variation on the timeline idea: Instead of a disk timeline you could have a
list of individual diskaccesses, and be able to select the ones you wanted to
@@ -407,42 +856,680 @@ Later:
- Optimization usecases:
- - A lot of stuff is read synchronously, but it is possible to read it asynchronously.
+ - A lot of stuff is read synchronously, but it is possible to read
+ it asynchronously.
Visualization: A timeline with alternating CPU/disk activity.
- - What function is doing all the synchronous reading, and what files/offsets is
- it reading. Visualization: lots of reads across different files out of one
- function
+ - What function is doing all the synchronous reading, and what
+ files/offsets is it reading. Visualization: lots of reads across
+ different files out of one function
- - A piece of the program is doing disk I/O. We can drop that entire piece of
- code. Sysprof visualization is ok, although seeing the files accessed is useful
- so that we can tell if those files are not just going to be used in
- other places. (Gnumeric plugin_init()).
+ - A piece of the program is doing disk I/O. We can drop that
+ entire piece of code. Sysprof visualization is ok, although seeing
+ the files accessed is useful so that we can tell if those files are
+ not just going to be used in other places. (Gnumeric plugin_init()).
- - A function is reading a file synchronously, but there is other (CPU/disk) stuff
- that could be done at the same time. Visualization: A piece of the timeline
- is diskbound with little or no CPU used.
+ - A function is reading a file synchronously, but there is other
+ (CPU/disk) stuff that could be done at the same time. Visualization:
+ A piece of the timeline is diskbound with little or no CPU used.
- - Want to improve code locality of library or binary. Visualization: no GUI, just
- produce a list of functions that should be put first in the file. Then run the
- program again until the list converges. (Valgrind may be more useful here).
+ - Want to improve code locality of library or binary. Visualization:
+ no GUI, just produce a list of functions that should be put first in
+ the file. Then run the program again until the list converges.
+ (Valgrind may be more useful here).
- - Nautilus reads a ton of files, icons + all the files in the homedirectory.
- Normal sysprof visualization is probably useful enough.
+ - Nautilus reads a ton of files, icons + all the files in the
+ homedirectory. Normal sysprof visualization is probably useful
+ enough.
- Profiling a login session.
- - Need to report stat() as well. (Where do inode data end up? In the buffer-cache?)
- Also open() may cause disk reads (seeks).
+ - Many applications are running at the same time, doing IPC. It would
+ be useful if we could figure out what other things a given process
+ is waiting on. Eg., in poll, find out what processes have the other
+ ends of the fd's open.
+ Visualization: multiple lines on a graph. Lines join up where
+ one process is blocking on another. That would show processes holding
+ up the progress very clearly.
+ This was suggested by Federico.
+
+ - Need to report stat() as well. (Where do inode data end up? In the
+ buffer-cache?) Also open() may cause disk reads (seeks).
+
+ - To generate the timeline we need to know when a disk request is
+ issued and when it is completed. This way we can assign blame to all
+ applications that have issued a disk request at a given point in time.
+
+ The disk timeline should probably vary in intensity with the number
+ of outstanding disk requests.
+
+
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ALREADY DONE: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+* Find out what is going on with kernel threads:
+
+ [(ksoftirqd/0)] 0.00 0.03
+ No map ([(ksoftirqd/0)]) 0.00 0.03
+ kernel 0.00 0.03
+ do_softirq 0.00 0.03
+ __do_softirq 0.00 0.03
+
+* Make sure there aren't leftover stacktraces from last time when
+ profiling begins.
+
+* Is the move-to-front in process_locate_map() really worth it?
+
+* Whenever we fail to lock the atomic variable, track this, and send the
+ information to userspace as an indication of the overhead of the profiling.
+ Although there is inherent aliasing here since stack scanning happens at
+ regular intervals.
- - To generate the timeline we need to know when a disk request is issued and when it
- is completed. This way we can assign blame to all applications that have issued a
- disk request at a given point in time.
+* Apparently, if you upgrade the kernel, then don't re-run configure,
+ the kernel Makefile will delete all of /lib/modules/<release>/kernel
+ if you run make install in the module directory. Need to find out what
+ is going on.
- The disk timeline should probably vary in intensity with the number of outstanding
- disk requests.
+* Performance:
+ Switching between descendant views is a slow:
+ - gtk_tree_store_get_path() is O(n^2) and accounts
+ for 43% of the time.
+ - GObject signal emission overhead accounts for 18% of
+ the time.
+ Consider adding a forked version of GtkTreeStore with
+ performance and bug fixes.
-DONE:
+* If we end up believing the kernel's own stacktraces, maybe
+ /proc/kallsyms shouldn't be parsed until the user hits profile.
+
+* Make it compilable against a non-running kernel.
+
+* With kernel module not installed, select Profiler->Start, then dismiss
+ the alert. This causes the start button to appear prelighted. Probably
+ just another gtk+ bug.
+
+- Fix bugs/performance issues:
+ - add_trace_to_tree() might be a little slow when dealing with deeply
+ recursive profiles. Hypothesis: seen_nodes can grow large, and the
+ algorithm is O(n^2) in the length of the trace.
+
+- Have kernel module report the file the address was found in
+ Should avoid a lot of potential broken/raciness with dlopen etc.
+ Probably better to send a list of maps with each trace. Which
+ shouldn't really be that expensive. We already walk the list of
+ maps in process_ensure_map() on every trace. And we can do hashing
+ if it turns out to be a problem.
+ Maybe replace the SysprofStackTrace with a union, so that
+ it can be either a list of maps, or a stacktrace. Both map lists and
+ stacktraces would come with a hashcode.allowing userspac. This avoids
+ the problem that maps could take up a lot of extra bandwidth.
+
+ struct MapInfo
+ {
+ long start;
+ long end;
+ long offset;
+ long inode;
+ }
+
+ struct Maps
+ {
+ int hash_code;
+ int n_maps;
+ MapInfo info [127];
+ char filenames [2048];
+ }
+
+- possibly add dependency on glib 2.8 if it is released at that point.
+ (g_file_replace())
+
+* Some notes about timer interrupt handling in Linux
+
+On an SMP system APIC is used - the interesting file is arch/i386/kernel/apic.c
+
+On UP systems, the normal IRQ0 is used
+When the interrupt happens,
+ entry.S
+ calls do_IRQ, which sets up the special interrupt stack,
+ and calls __do_IRQ, which is in /kernel/irq/handle.c.
+ This calls the corresponding irqaction, which has previously
+ been setup by arch/i386/mach-default/setup.c to point to
+ timer_interrupt, which is in arch/i386/kernel/time.c.
+ This calls do_timer_interrupt_hooks() which is defined in
+ /include/asm-i386/mach-default/do_timer.h. This function
+ then calls profile_tick().
+
+ Note when the CPU switches from user mode to kernel mode, it
+ pushes SS/ESP on top of the kernel stack, but when it switches
+ from kernel mode to kernel mode, it does _not_ push SS/ESP.
+ It does in both cases push EIP though.
+
+* Rename sysprof-text to sysprof-cli
+
+* Find out why the samples label won't right adjust
+
+* It crashes sometimes.
+
+ I haven't seen any crashes in a long time
+
+* Find out why the strings
+
+ _ZL11DisplayLineP20nsDisplayListBuilderRK6nsRectS3_R19nsLineList_iteratoriRiRK16nsDisplayListSetP12nsBlockFrame
+ _ZL11DisplayRowsP20nsDisplayListBuilderP7nsFrameRK6nsRectRK16nsDisplayListSet _ZL11DrawBordersP10gfxContextR7gfxRectS2_PhPdS4_PjPP14nsBorderColorsijiP6nsRect _ZL11HandleEventP10nsGUIEvent
+ _ZL12IsContentLEQP13nsDisplayItemS0_Pv
+ _ZL15expose_event_cbP10_GtkWidgetP15_GdkEventExpose
+
+ do not get demangled.
+
+* For glibc, the debug files do not contain .strtab and .symtab, but
+ the original files do. The algorithm in binfile.c must be modified
+ accordingly.
+
+* If we profile something that is not very CPU bound, sysprof itself
+ seems to get a disproportionate amount of the samples. Should look
+ into this. Fixed by only returning from poll when there is more
+ than eight traces available.
+
+* regarding crossing system call barriers: Find out about the virtual dso
+ that linux uses to do fast system calls:
+
+ http://lkml.org/lkml/2002/12/18/218
+
+ and what that actually makes the stack look like. (We may want to just
+ special case this fake dso in the symbol lookup code).
+
+ Maybe get_user_pages() is the way forward at least for some stuff.
+
+ note btw. that the kernel pages are only one or two pages, so we
+ could easily just dump them to userspace.
+
+* In profile.c, change "non_recursive" to "cumulative", and
+ "marked_non_recursive" to a boolean "charged". This is tricky code,
+ so be careful. Possibly make it a two-pass operation:
+ - first add the new trace
+ - then walk from the leaf, charging nodes
+ That would allow us to get rid of the marked field altogether. In fact,
+ maybe the descendants tree could become a stackstash. We'll just have
+ to make stack_stash_add_trace() return the leaf.
+
+ DONE: the name is now "cumulative"
+
+
+* vdso
+ - assume its the same across processes, just look at
+ sysprof's own copy.
+ Done: vdso is done now
+ - send copy of it to userspace once, or for every
+ sample.
+
+* Various:
+ - 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.
+ Seems to mostly happen when multiple processes are
+ involved.
+ - Numbers in caller view are completely screwed up.
+ - It looks like it sometimes gets confused with similar but different
+ processes: 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.
+
+* commandline version should check that the output file is writable
+ before starting the profiling.
+
+* See if we can reproduce the problem where libraries didn't get correctly
+ reloaded after new versions were installed.
+ This is just the (deleted) problem. Turns out that the kernel
+ doesn't print (deleted) in all cases. Some possibilities:
+
+ - check that the inodes of the mapped file and the disk file
+ are the same (done in HEAD).
+
+ - check that the file was not modified after being mapped?
+ (Can we get the time it was mapped or opened?) If it was
+ modified you'd expect the inode to change, right?
+
+* Find out if the first sort order of a GtkTreeView column can be
+ changed programmatically. It can't (and the GTK+ bug was wontfixed).
+ A workaround is possible though. (Someone, please write a
+ GtkTreeView replacement!)
+
+* Missing things in binparser.[ch]
+
+ - maybe convert BIN_UINT32 => { BIN_UINT, 4 }
+ we already have the width in the struct.
+
+* Rethink binparser. Maybe the default mode should be:
+ - there is a current offset
+ - you can move the cursor
+ - _goto()
+ - _align()
+ - you can read structs with "begin_struct (format) / end_struct()"
+ Or maybe just "set_format()" that would accept NULL?
+ - when you are reading a struct, you can skip records with _index()
+ - you can read fields with get_string/get_uint by passing a name.
+ - you can read anonymous strings and uints by passing NULL for name
+ This is allowed even when not reading structs. Or maybe this
+ should be separate functions. Advantages:
+ - they can skip ahead, unlike fields accessors
+ - you can access specific types (8,16,32,64)
+ - there is no "name" field
+ Disadvantage:
+ - the field accesors would need renaming.
+ bin_parser_get_uint_field ()
+ is not really that bad though.
+ Maybe begin_record() could return a structure you could
+ use to access that particular record? Would nicely solve
+ the problems with "goto" and "index".
+ bin_record_get_uint();
+ What should begin/end be called? They will have different
+ objects passed.
+ bin_parser_get_record (parser) -> record
+ bin_record_free (record);
+ - Maybe support for indirect strings? Ie., get_string() in elfparser
+ - This will require endianness to be a per-parser property. Which is
+ probably just fine. Although d-bus actually has
+ per-message endianness. Maybe there could be a settable
+ "endianness" property.
+
+* Don't look in $(libdir) for separate debug files (since $libdir is
+ the libdir for sysprof, not a system wide libdir). Tim Rowley.
+ Fix is probably to hardcode /usr/lib, and also look in $libdir.
+
+* Consider deleting cmdline hack in process.c and replace with something at
+ the symbol resolution level. Will require more memory though. DONE: in
+ head, processes are no longer coalesced based on cmdline. Need to add something
+ at the symbol level.
+
+* don't loop infinitely if there are cycles in the debuglink graph.
+
+* Add "sysprof --version"
+
+* Fix (potential) performance issues in symbol lookup.
+
+- when an elf file is read, it should be checked that the various
+ sections are of the right type. For example the debug information
+ for emacs is just a stub file where all the sections are NOBITS.
+
+* Try reproducing crash when profiling xrender demo
+ - it looks like it crashes when it attempts to read /usr/bin/python
+ - apparently what's going on is that one of the symbols in python's
+ dynamic symbol table has a completely crazy 'st_name' offset.
+ DONE: we didn't actually need to read the name at all,
+ but still should find out why that value is so weird.
+ It looks like there is something strange going on with that file.
+ All the dynsyms have weird info/type values, yet nm and readelf
+ have no problems displaying it.
+
+- Can .gnu_debuglink recurse?
+ yes, it can, and we should probably not crash if there are
+ cycles in the graph.
+
+* Find out why we are getting bogus symbols reported for /usr/bin/Xorg
+ Like this:
+
+ Everything 0.00 100.00
+ [/usr/bin/Xorg] 0.00 94.79
+ GetScratchPixmapHeader 0.00 94.79
+ __libc_start_main 0.00 94.79
+ FindAllClientResources 0.00 94.79
+ FreeFontPath 0.00 94.79
+ SProcRenderCreateConicalGradient 0.00 94.56
+ ProcRenderTrapezoids 0.00 94.56
+ AllocatePicture 0.00 94.56
+ __glXDispatch 0.00 0.16
+ __glXVendorPrivate 0.00 0.08
+ __glXRender 0.00 0.08
+ SmartScheduleStartTimer 0.00 0.08
+ [./sysprof] 0.00 2.76
+ [sshd: ssp@pts/0] 0.00 2.37
+ [compiz] 0.00 0.08
+
+ What's going on here is that the computed load address for the X server
+ binary is different for the debug binary. The lowest allocated address
+ is 0x08047134 for the normal, and 0x8048134 for the debug. But it looks
+ like the addresses are still the same for the symbols.
+ The root of this problem may be that we should ignore the load
+ address of the debug binary, and just lookup the address computed.
+ The *text* segments have the same address though. Everything from
+ "gnu version" on has the same address.
+
+ So:
+ - find out where in memory the text segment is
+ - take an address and compute its offset to the text segment
+ - in elf parser, find address of text segment
+ - add offset
+ - lookup resulting address
+
+So basically, "load address" should really be text address. Except of course
+that load_address is not used in process.c - instead the 'load address' of the
+first part of the file is computed and assumed to be equivalent to the
+load address. So to lookup something you probably actually need
+to know the load/offset at the elf parser level:
+
+ lookup_symbol (elf, map, offset, address)
+
+then,
+
+ real load address of text (lta) = map - offset + text_offset
+
+ offset of called func (ocf): addr - lta
+
+ thing to lookup in table: ocf + text_addr.loadaddr in debug
+
+ addr - map - offset + text_offset
+
+
+ hmmm ...
+
+* plug all the leaks
+ - don't leak the presentation strings/objects
+ - maybe add stack_stash_set_free_func() or something
+* Delete elf_parser_new() and rename elf_parser_new_from_file()
+
+* Add demangling again
+
+* Restore filename => binfile cache.
+
+* It is apparently possible to get another timer interrupt in the middle
+ of timer_notify. If this happens the circular buffer gets screwed up and
+ you get crashes. Note this gets much worse on SMP (in fact how did this
+ work at all previously?)
+
+ Possible fixes
+ - have a "in timer notify" variable, then simply reject nested
+ interrupts
+ - keep a "ghost head" that timers can use to allocate new traces,
+ then update the real head whenever one of them completes. Note
+ though, that the traces will get generated in the wrong order
+ due to the nesting. In fact, only the outermost timernotify
+ can update the real head, and it should update it to ghost_head.
+ - do correct locking? Nah, that's crazy talk
+ Also note: a race is a race, and on SMP we probably can't even make it
+ unlikely enough to not matter.
+
+ Fixed by ignoring the nested interrupts using an atomic variable.
+
+* When you load a file from the commandline, there is a weird flash of the toolbar.
+ What's going on is this:
+ - this file is loaded into a tree model
+ - the tree model is set for the function list
+ - this causes the selection changed signal to be emitted
+ - the callback for that signal process updates
+ - somehow in that update process, the toolbar flashes.
+ - turns out to be a gtk+ issue: 350517
+
+- screenshot window must be cleared when you press start.
+
+- Formats should become first-class, stand-alone objects that offers
+ help with parsing and nothing else.
+
+ ParseContext* format_get_parse_context (format, err);
+ gboolean parse_context_begin (parse_context, name, err);
+ gboolean parse_context_end (parse_format, name, err);
+
+ basically, a Format encapsulates a DFA, and a ParseContext encapsulates
+ the current state.
+
+- make stackstash ref counted
+
+- Charge 'self' properly to processes that don't get any stack trace at all
+ (probably we get that for free with stackstash reorganisation)
+
+- CVS head now has two radio buttons in the right pane, and
+ caller pane is gone. (This turned out to be a bad idea, because it
+ is often useful to click on ancestors to move up the tree).
+
+* Don't build the GUI if gtk+ is not installed
+
+* Find out why we sometimes get reports of time spent by [pid 0].
+
+* - Run a.out generated normally with gcc.
+ - Run sysprof
+ - hit profile
+ - Kill a.out
+ - strip a.out
+ - run a.out
+ - hit start
+ - hit profile
+ At this point we should not get any symbols, but we do. There is some
+ sort of bad caching going on.
+
+* support more than one reader of the samples properly
+ - Don't generate them if noone cares
+
+* Correctness
+ - When the module is unloaded, kill all processes blocking in read
+ - or block unloading until all processes have exited
+ Unfortunately this is basically impossible to do with a /proc
+ file (no open() notification). So, for 1.0 this will have to be
+ a dont-do-that-then. For 1.2, we should do it with a sysfs and
+ kobject instead.
+
+ - When the module is unloaded, can we somehow *guarantee* that no
+ kernel thread is active? Doesn't look like it; however we can
+ get close by decreasing a ref count just before returning
+ from the module. (There may still be return instructions etc.
+ that will get run). This may not be an issue with the timer
+ based scanning we are using currently.
+
+* Find out why we get hangs with rawhide kernels. This only happens with the
+ 'trace "current"' code. See this mail:
+
+ http://mail.nl.linux.org/kernelnewbies/2005-08/msg00157.html
+
+ esp0 points to top of kernel stack
+ esp points to top of user stack
+
+ (Reported by Kjartan Maraas).
+
+- When not profiling, sysprof shouldn't keep the file open.
+
+- Make things faster
+ - Can I get it to profile itself?
+ - speedprof seems to report that lots of time is spent in
+ stack_stash_foreach() and also in generate_key()
+- add an 'everything' object. It is really needed for a lot of things
+ - should be easy to do with stackstash reorganization.
+
+
+* Handle time being set back in the RESET_DEAD_PERIOD code.
+
+- total should probably be cached so that compute_total() doesn't
+ take 80% of the time to generate a profile.
+
+- Fixing the oops in kernels < 2.6.11
+
+ - Probably just require 2.6.11 (necessary for timer interrupt
+ based anyway).
+
+ - Make the process waiting in poll() responsible for extracting
+ the backtrace. Give a copy of the entire stack rather than doing
+ the walk inside the kernel.
+
+ New model:
+ - Two arrays,
+ one of actual scanned stacks
+ one of tasks that need to be scanned
+ One wait queue,
+ wait for data
+
+ - in read() wait for stack data:
+ scan_tasks()
+ if (!stack_data)
+ return -EWOULDBLOCK;
+
+ in poll()
+ while (!stack data) {
+ wait_for_data();
+ scan_tasks();
+ }
+ return READABLE;
+
+ scan_tasks() is a function that converts waiting
+ tasks into data, and wakes them up.
+
+ - in timer interrupt:
+
+ if (someone waiting in poll() &&
+ current && current != that_someone &&
+ current is runnable)
+ {
+ stop current;
+ add current to queue;
+ wake wait_for_data;
+ }
+
+ This way, we will have a real userspace process
+ that can take the page faults.
+
+
+ - Different approach:
+
+ pollable file where a regular userspace process
+ can read a pid. Any pid returned is guaranteed to be
+ UNINTERRUPTIBLE. Userspace process is required to
+ start it again when it is done with it.
+
+ Also provide interface to read arbitrary memory of
+ that process.
+
+ ptrace() could in principle do all this, but
+ unfortunately it sucks to continuously
+ ptrace() processes.
+
+ - Yet another
+
+ Userspace process can register itself as "profiler"
+ and pass in a filedescriptor where all sorts of
+ information is sent.
+
+ - could tie lifetime of module to profiler
+ - could send "module going away" information
+ - Can we map filedescriptors to files in
+ a module?
+
+* Make sure sysprof-text is not linked to gtk+
+
+* Consider renaming profiler.[ch] to collector.[ch]
+
+* Crash reported by Rudi Chiarito with n_addrs == 0.
+
+* Find out what distributions it actually works on
+ (ask for sucess/failure-stories in 1.0 releases)
+
+* Add note in README about Ubuntu and Debian -dbg packages and how to get
+ 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:
+
+ - Update version numbers in source
+
+ - Make tarball
+
+ - Check that tarball works
+
+ - cvs commit
+
+ - cvs tag sysprof-1-0
+
+ - Update website
+
+ - Announce on Freshmeat
+
+ - Announce on gnome-announce
+ - Announce on kernel list.
+
+ - Announce on Gnomefiles
+
+ - Announce on news.gnome.org
+ - Send to slashdot/developers
+ - Announce on devtools list (?)
+
+ - Announce on Advogato
+ link to archive
+
+* The handling of the global variable in signal-handler.[ch] needs to be
+ atomic - right now it isn't. The issue is what happens if a handled signal
+ arrives while we are manipulating the list?
+
+* (User space stack must probably be done in a thread - kernel
+ stack must probably be taken in the interrupt itself?
+ - Why this difference? The page tables should still be loaded. Is it
+ because pages_present() doesn't work? No, turning it off doesn't help.
+ - It looks like this works. Get:
+
+ struct pt_regs *user_regs =
+ (void *)current->thread.esp0 - sizeof (struct pt_regs);
+
+ then use pages_present as usual to trace with user_regs; There could be
+ rare lockups though.
+
+* Non-GUI version that can save in a format the GUI can understand.
+ Could be used for profiling startup etc. Would preferably be able to
+ dump the data to a network socket. Should be able to react to eg.
+ SIGUSR1 by dumping the data.
+
+ Work done by Lorenzo:
+
+ http://www.colitti.com/lorenzo/software/gnome-startup/sysprof-text.diff
+ http://www.colitti.com/lorenzo/software/gnome-startup/sysprof.log
+ http://colitti.com/lorenzo/software/gnome-startup/
* consider caching [filename => bin_file]
diff --git a/binfile.c b/binfile.c
index 4dcd1b8..95613db 100644
--- a/binfile.c
+++ b/binfile.c
@@ -3,7 +3,7 @@
* Copyright 2002, Kristian Rietveld
*
* Sysprof -- Sampling, systemwide CPU profiler
- * Copyright 2004, 2005, 2008 Soeren Sandmann
+ * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,587 +23,492 @@
/* Most interesting code in this file is lifted from bfdutils.c
* and process.c from Memprof,
*/
-
+#include "config.h"
+
#include <glib.h>
-#include "binfile.h"
-#include <bfd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
-static void bfd_nonfatal (const char *string);
-static void bfd_fatal (const char *string);
+#include "binfile.h"
+#include "elfparser.h"
+#include "util.h"
-/* Binary File */
-struct BinFile
+struct bin_file_t
{
- char * filename;
- int n_symbols;
- Symbol *symbols;
- Symbol undefined;
- int ref_count;
+ int ref_count;
+
+ GList * elf_files;
+
+ char * filename;
+
+ char * undefined_name;
+
+ gulong text_offset;
+
+ gboolean inode_check;
+ ino_t inode;
};
-static bfd *
-open_bfd (const char *file)
+static ino_t
+read_inode (const char *filename)
{
- bfd *abfd = bfd_openr (file, NULL);
+ struct stat statbuf;
- if (!abfd)
- return NULL;
+ if (strcmp (filename, "[vdso]") == 0)
+ return (ino_t)0;
- if (!bfd_check_format (abfd, bfd_object))
- {
- bfd_close (abfd);
- return NULL;
- }
+ if (stat (filename, &statbuf) < 0)
+ return (ino_t)-1;
- return abfd;
+ return statbuf.st_ino;
}
-static unsigned long
-calc_crc32 (unsigned long crc, unsigned char *buf, size_t len)
-{
- static const unsigned long crc32_table[256] = {
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
- 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
- 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
- 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
- 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
- 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
- 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
- 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
- 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
- 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
- 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
- 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
- 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
- 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
- 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
- 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
- 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
- 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
- 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
- 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
- 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
- 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
- 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
- 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
- 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
- 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
- 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
- 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
- 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
- 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
- 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
- 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
- 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
- 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
- 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
- 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
- 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
- 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
- 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
- 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
- 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
- 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
- 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
- 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
- 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
- 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
- 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
- 0x2d02ef8d
- };
- unsigned char *end;
-
- crc = ~crc & 0xffffffff;
- for (end = buf + len; buf < end; ++buf)
- crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
- return ~crc & 0xffffffff;;
-}
-
-static char *
-get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
+static gboolean
+already_warned (const char *name)
{
- asection *sect;
- ssize_t debuglink_size;
- unsigned long crc32;
- char *contents;
- int crc_offset;
-
- sect = bfd_get_section_by_name (abfd, ".gnu_debuglink");
+ static GPtrArray *warnings;
+ int i;
- if (sect == NULL)
- return NULL;
+ if (!warnings)
+ warnings = g_ptr_array_new ();
- debuglink_size = bfd_get_section_limit (abfd, sect);
-
- if (debuglink_size < 6)
- {
- g_warning ("%s: .gnu_debuglink section is %d bytes long",
- abfd->filename, debuglink_size);
- return NULL;
- }
-
- contents = g_malloc (debuglink_size);
- bfd_get_section_contents (abfd, sect, contents,
- (file_ptr)0, (bfd_size_type)debuglink_size);
-
- /* Sanity check */
- if (!memchr (contents, '\0', debuglink_size - 4))
+ for (i = 0; i < warnings->len; ++i)
{
- g_warning ("%s: Malformed .gnu_debuglink section", abfd->filename);
-
- g_free (contents);
- return NULL;
+ if (strcmp (warnings->pdata[i], name) == 0)
+ return TRUE;
}
-
- /* Crc value is stored after the filename, aligned up to 4 bytes. */
- crc_offset = strlen (contents) + 1;
- crc_offset = (crc_offset + 3) & ~3;
- crc32 = bfd_get_32 (abfd, (bfd_byte *) (contents + crc_offset));
+ g_ptr_array_add (warnings, g_strdup (name));
- *crc32_out = crc32;
- return contents;
+ return FALSE;
}
-static gboolean
-separate_debug_file_exists (const char *name, unsigned long crc)
+static const char *const debug_file_directory = DEBUGDIR;
+
+static ElfParser *
+get_build_id_file (ElfParser *elf)
{
- unsigned long file_crc = 0;
- int fd;
- guchar buffer[8*1024];
- int count;
+ const char *build_id;
+ GList *tries = NULL, *list;
+ char *init, *rest;
+ ElfParser *result = NULL;
+ char *tmp;
- fd = open (name, O_RDONLY);
- if (fd < 0)
- return 0;
+ build_id = elf_parser_get_build_id (elf);
- while ((count = read (fd, buffer, sizeof (buffer))) > 0)
- file_crc = calc_crc32 (file_crc, buffer, count);
+ if (!build_id)
+ return NULL;
- close (fd);
-
- if (crc != file_crc)
- {
- g_print ("warning: %s has wrong crc\n", name);
- return FALSE;
- }
- else
+ if (strlen (build_id) < 4)
+ return NULL;
+
+ init = g_strndup (build_id, 2);
+ rest = g_strdup_printf ("%s%s", build_id + 2, ".debug");
+
+ tmp = g_build_filename (
+ "/usr", "lib", "debug", ".build-id", init, rest, NULL);
+ tries = g_list_append (tries, tmp);
+
+ tmp = g_build_filename (
+ debug_file_directory, ".build-id", init, rest, NULL);
+ tries = g_list_append (tries, tmp);
+
+ for (list = tries; list != NULL; list = list->next)
{
- return TRUE;
+ char *name = list->data;
+ ElfParser *parser = elf_parser_new (name, NULL);
+
+ if (parser)
+ {
+ const char *file_id = elf_parser_get_build_id (parser);
+
+ if (file_id && strcmp (build_id, file_id) == 0)
+ {
+ result = parser;
+ break;
+ }
+
+ elf_parser_free (parser);
+ }
}
+
+ g_list_foreach (tries, (GFunc)g_free, NULL);
+ g_list_free (tries);
+
+ g_free (init);
+ g_free (rest);
+
+ return result;
}
-/* FIXME - not10: this should probably be detected by config.h -- find out what gdb does*/
-static const char *debug_file_directory = "/usr/lib/debug";
-
-static char *
-find_separate_debug_file (bfd *abfd)
+static ElfParser *
+get_debuglink_file (ElfParser *elf,
+ const char *filename,
+ char **new_name)
{
- char *basename;
+#define N_TRIES 4
+ const char *basename;
char *dir;
- char *debugfile;
- unsigned long crc32;
+ guint32 crc32;
+ GList *tries = NULL, *list;
+ ElfParser *result = NULL;
- basename = get_debug_link_info (abfd, &crc32);
- if (basename == NULL)
+ if (!elf)
return NULL;
- dir = g_path_get_dirname (bfd_get_filename (abfd));
+ basename = elf_parser_get_debug_link (elf, &crc32);
- /* First try in the same directory as the original file: */
- debugfile = g_build_filename (dir, basename, NULL);
- if (separate_debug_file_exists (debugfile, crc32))
- {
- g_free (basename);
- g_free (dir);
- return debugfile;
- }
- g_free (debugfile);
+#if 0
+ g_print (" debug link for %s is %s\n", filename, basename);
+#endif
- /* Then try in a subdirectory called .debug */
- debugfile = g_build_filename (dir, ".debug", basename, NULL);
- if (separate_debug_file_exists (debugfile, crc32))
- {
- g_free (basename);
- g_free (dir);
- return debugfile;
- }
- g_free (debugfile);
+ if (!basename)
+ return NULL;
- /* Then try in the global debugfile directory */
- debugfile = g_build_filename (debug_file_directory, dir, basename, NULL);
- if (separate_debug_file_exists (debugfile, crc32))
+ dir = g_path_get_dirname (filename);
+
+ tries = g_list_append (tries, g_build_filename (dir, basename, NULL));
+ tries = g_list_append (tries, g_build_filename (dir, ".debug", basename, NULL));
+ tries = g_list_append (tries, g_build_filename ("/usr", "lib", "debug", dir, basename, NULL));
+ tries = g_list_append (tries, g_build_filename (debug_file_directory, dir, basename, NULL));
+
+ for (list = tries; list != NULL; list = list->next)
{
- g_free (basename);
- g_free (dir);
- return debugfile;
+ const char *name = list->data;
+ ElfParser *parser = elf_parser_new (name, NULL);
+ guint32 file_crc;
+
+ if (parser)
+ {
+ file_crc = elf_parser_get_crc32 (parser);
+
+ if (file_crc == crc32)
+ {
+ result = parser;
+ *new_name = g_strdup (name);
+ break;
+ }
+ else
+ {
+ if (!already_warned (name))
+ g_print ("warning: %s has wrong crc \n", name);
+ }
+
+ elf_parser_free (parser);
+ }
}
- g_free (debugfile);
- g_free (basename);
g_free (dir);
- return NULL;
+
+ g_list_foreach (tries, (GFunc)g_free, NULL);
+ g_list_free (tries);
+
+ return result;
}
-static asymbol **
-slurp_symtab (bfd *abfd, long *symcount)
+static GList *
+get_debug_binaries (GList *files,
+ ElfParser *elf,
+ const char *filename)
{
- asymbol **sy = (asymbol **) NULL;
- long storage;
+ ElfParser *build_id_file;
+ GHashTable *seen_names;
+ GList *free_us = NULL;
+
+ build_id_file = get_build_id_file (elf);
+
+ if (build_id_file)
+ return g_list_prepend (files, build_id_file);
+
+ /* .gnu_debuglink is actually a chain of debuglinks, and
+ * there have been real-world cases where following it was
+ * necessary to get useful debug information.
+ */
+ seen_names = g_hash_table_new (g_str_hash, g_str_equal);
- if (!(bfd_get_file_flags (abfd) & HAS_SYMS))
+ while (elf)
{
- *symcount = 0;
- return NULL;
+ char *debug_name;
+
+ if (g_hash_table_lookup (seen_names, filename))
+ break;
+
+ g_hash_table_insert (seen_names, (char *)filename, (char *)filename);
+
+ elf = get_debuglink_file (elf, filename, &debug_name);
+
+ if (elf)
+ {
+ files = g_list_prepend (files, elf);
+ free_us = g_list_prepend (free_us, debug_name);
+ filename = debug_name;
+ }
}
- storage = bfd_get_symtab_upper_bound (abfd);
- if (storage < 0)
- bfd_fatal (bfd_get_filename (abfd));
-
- if (storage)
- sy = (asymbol **) malloc (storage);
-
- *symcount = bfd_canonicalize_symtab (abfd, sy);
- if (*symcount < 0)
- bfd_fatal (bfd_get_filename (abfd));
+ g_list_foreach (free_us, (GFunc)g_free, NULL);
+ g_list_free (free_us);
- return sy;
-}
-
-extern char *cplus_demangle (const char *mangled, int options);
-
-#define DMGL_PARAMS (1 << 0) /* Include function args */
-#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
-
-static char *
-demangle (bfd *bfd, const char *name)
-{
- char *demangled;
-
- if (bfd_get_symbol_leading_char (bfd) == *name)
- ++name;
-
- demangled = cplus_demangle (name, DMGL_ANSI | DMGL_PARAMS);
- return demangled ? demangled : g_strdup (name);
-}
-
-static gint
-compare_address (const void *a, const void *b)
-{
- const Symbol *symbol1 = a;
- const Symbol *symbol2 = b;
-
- if (symbol1->address < symbol2->address)
- return -1;
- else if (symbol1->address == symbol2->address)
- return 0;
- else
- return 1;
+ g_hash_table_destroy (seen_names);
+
+ return files;
}
-static void
-read_symbols (BinFile *bf)
+static char **
+get_lines (const char *format, pid_t pid)
{
- asection *text_section;
- const char *separate_debug_file;
- asymbol **bfd_symbols;
- long n_symbols;
- int i;
- bfd *bfd;
- GArray *symbols;
- file_ptr real_text_offset;
-
- bf->symbols = NULL;
- bf->n_symbols = 0;
-
- bfd = open_bfd (bf->filename);
- if (!bfd)
- return;
-
- text_section = bfd_get_section_by_name (bfd, ".text");
- if (!text_section)
- {
- bfd_close (bfd);
- return;
- }
-
- /* Offset of the text segment in the real binary (not the debug one) */
- real_text_offset = text_section->filepos;
+ char *filename = g_strdup_printf (format, pid);
+ char **result = NULL;
+ char *contents;
- separate_debug_file = find_separate_debug_file (bfd);
- if (separate_debug_file)
+ if (g_file_get_contents (filename, &contents, NULL, NULL))
{
- bfd_close (bfd);
- bfd = open_bfd (separate_debug_file);
-#if 0
- g_print ("bfd for %s is %s\n", bf->filename, separate_debug_file);
-#endif
- if (!bfd)
- return;
+ result = g_strsplit (contents, "\n", -1);
+
+ g_free (contents);
}
- bfd_symbols = slurp_symtab (bfd, &n_symbols);
+ g_free (filename);
- if (!bfd_symbols)
- return;
-
- text_section = bfd_get_section_by_name (bfd, ".text");
- if (!text_section)
- return;
-
- symbols = g_array_new (FALSE, FALSE, sizeof (Symbol));
-
- /* g_print ("%s: text vma: %x\n", bf->filename, text_section->vma); */
+ return result;
+}
- for (i = 0; i < n_symbols; i++)
+static const uint8_t *
+get_vdso_bytes (size_t *length)
+{
+ static const uint8_t *bytes = NULL;
+ static size_t n_bytes = 0;
+ static gboolean has_data;
+
+ if (!has_data)
{
- Symbol symbol;
+ char **lines = get_lines ("/proc/%d/maps", getpid());
+ int i;
- if ((bfd_symbols[i]->flags & BSF_FUNCTION) &&
- (bfd_symbols[i]->section == text_section))
+ for (i = 0; lines[i] != NULL; ++i)
{
- /* Store the address in
- *
- * "offset into text_segment + filepos of text segment in original binary"
- *
- * Ie., "file position of original binary"
- */
-
- /* offset into text section */
- symbol.address =
- bfd_asymbol_value (bfd_symbols[i]) - text_section->vma + real_text_offset;
+ char file[256];
+ gulong start;
+ gulong end;
+ int count = sscanf (
+ lines[i], "%lx-%lx %*15s %*x %*x:%*x %*u %255s",
+ &start, &end, file);
- symbol.name = demangle (bfd, bfd_asymbol_name (bfd_symbols[i]));
-#if 0
- g_print ("computed address for %s: %lx\n", symbol.name, symbol.address);
-#endif
- g_array_append_vals (symbols, &symbol, 1);
+ if (count == 3 && strcmp (file, "[vdso]") == 0)
+ {
+ n_bytes = end - start;
+
+ /* Dup the memory here so that valgrind will only
+ * report one 1 byte invalid read instead of
+ * a ton when the elf parser scans the vdso
+ *
+ * The reason we get a spurious invalid read from
+ * valgrind is that we are getting the address directly
+ * from /proc/maps, and valgrind knows that its mmap()
+ * wrapper never returned that address. But since it
+ * is a legal mapping, it is legal to read it.
+ */
+ bytes = g_memdup ((uint8_t *)start, n_bytes);
+
+ has_data = TRUE;
+ }
}
}
-
- if (n_symbols)
- free (bfd_symbols);
-
-#if 0
- if (!n_symbols)
- g_print ("no symbols found for %s\n", bf->filename);
- else
- g_print ("symbols found for %s\n", bf->filename);
-#endif
- /* Sort the symbols by address */
- qsort (symbols->data, symbols->len, sizeof(Symbol), compare_address);
-
- bf->n_symbols = symbols->len;
- bf->symbols = (Symbol *)g_array_free (symbols, FALSE);
-}
-
-static GHashTable *bin_files;
+ if (length)
+ *length = n_bytes;
+
+ return bytes;
+}
-BinFile *
+bin_file_t *
bin_file_new (const char *filename)
{
- BinFile *bf;
+ ElfParser *elf = NULL;
+ bin_file_t *bf;
- if (!bin_files)
- bin_files = g_hash_table_new (g_str_hash, g_str_equal);
-
- bf = g_hash_table_lookup (bin_files, filename);
- if (bf)
+ bf = g_new0 (bin_file_t, 1);
+
+ bf->inode_check = FALSE;
+ bf->filename = g_strdup (filename);
+ bf->undefined_name = g_strdup_printf ("In file %s", filename);
+ bf->ref_count = 1;
+ bf->elf_files = NULL;
+
+ if (strcmp (filename, "[vdso]") == 0)
{
- bf->ref_count++;
+ const guint8 *vdso_bytes;
+ gsize length;
+
+ vdso_bytes = get_vdso_bytes (&length);
+
+ if (vdso_bytes)
+ elf = elf_parser_new_from_data (vdso_bytes, length);
}
else
{
- bf = g_new0 (BinFile, 1);
+ elf = elf_parser_new (filename, NULL);
+ }
- bf->filename = g_strdup (filename);
+ if (elf)
+ {
+ /* We need the text offset of the actual binary, not the
+ * (potential) debug binaries
+ */
+ bf->text_offset = elf_parser_get_text_offset (elf);
- read_symbols (bf);
+ bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename);
+ bf->elf_files = g_list_append (bf->elf_files, elf);
- bf->undefined.name = g_strdup_printf ("In file %s", filename);
- bf->undefined.address = 0x0;
- bf->ref_count = 1;
- g_hash_table_insert (bin_files, bf->filename, bf);
+ bf->inode = read_inode (filename);
}
-
+
return bf;
}
void
-bin_file_free (BinFile *bf)
+bin_file_free (bin_file_t *bin_file)
{
- if (--bf->ref_count == 0)
+ if (--bin_file->ref_count == 0)
{
- int i;
-
- g_hash_table_remove (bin_files, bf->filename);
+ g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL);
+ g_list_free (bin_file->elf_files);
- g_free (bf->filename);
-
- for (i = 0; i < bf->n_symbols; ++i)
- g_free (bf->symbols[i].name);
- g_free (bf->symbols);
- g_free (bf->undefined.name);
- g_free (bf);
+ g_free (bin_file->filename);
+ g_free (bin_file->undefined_name);
+ g_free (bin_file);
}
}
-/**
- * bin_file_lookup_symbol:
- * @bf: A BinFile
- * @address: The address to lookup
- *
- * Look up a symbol. @address should be in file coordinates
- *
- **/
-const Symbol *
-bin_file_lookup_symbol (BinFile *bf,
- gulong address)
+const bin_symbol_t *
+bin_file_lookup_symbol (bin_file_t *bin_file,
+ gulong address)
{
- int first = 0;
- int last = bf->n_symbols - 1;
- int middle = last;
- Symbol *data;
- Symbol *result;
+ GList *list;
- if (!bf->symbols || (bf->n_symbols == 0))
- return &(bf->undefined);
+#if 0
+ g_print ("-=-=-=- \n");
+
+ g_print ("bin file lookup lookup %d\n", address);
+#endif
+
+ address -= bin_file->text_offset;
- data = bf->symbols;
-
#if 0
- g_print ("looking up %p in %s ", address, bf->filename);
+ g_print ("lookup %d in %s\n", address, bin_file->filename);
#endif
- if (address < data[last].address)
+ for (list = bin_file->elf_files; list != NULL; list = list->next)
{
- /* Invariant: data[first].addr <= val < data[last].addr */
+ ElfParser *elf = list->data;
+ const ElfSym *sym = elf_parser_lookup_symbol (elf, address);
- while (first < last - 1)
+ if (sym)
{
- middle = (first + last) / 2;
- if (address < data[middle].address)
- last = middle;
- else
- first = middle;
- }
- /* Size is not included in generic bfd data, so we
- * ignore it for now. (It is ELF specific)
- */
- result = &data[first];
- }
- else
- {
- result = &data[last];
- }
-
#if 0
- g_print ("-> %s\n", result->name);
+ g_print ("found %lx => %s\n", address,
+ bin_symbol_get_name (bin_file, sym));
#endif
+ return (const bin_symbol_t *)sym;
+ }
+ }
- /* If the name is "call_gmon_start", the file probably doesn't
- * have any other symbols
- */
- if (strcmp (result->name, "call_gmon_start") == 0)
- return &(bf->undefined);
- else if (strncmp (result->name, "__do_global_ctors_aux", strlen ("__do_global_ctors_aux")) == 0)
- {
#if 0
- g_print ("ctors: %p, pos: %p\n", address, result->address);
+ g_print ("%lx undefined in %s (textoffset %x)\n",
+ address + bin_file->text_offset,
+ bin_file->filename,
+ bin_file->text_offset);
#endif
- }
- return result;
-}
-
-
-/* Symbol */
-Symbol *
-symbol_copy (const Symbol *orig)
-{
- Symbol *copy;
-
- copy = g_new (Symbol, 1);
- copy->name = g_strdup (orig->name);
- copy->address = orig->address;
-
- return copy;
+ return (const bin_symbol_t *)bin_file->undefined_name;
}
gboolean
-symbol_equal (const void *sa,
- const void *sb)
+bin_file_check_inode (bin_file_t *bin_file,
+ ino_t inode)
{
- const Symbol *syma = sa;
- const Symbol *symb = sb;
+ if (bin_file->inode == inode)
+ return TRUE;
- if (symb->address != syma->address)
+ if (!bin_file->elf_files)
return FALSE;
-
- /* symbols compare equal if their names are both NULL */
- if (!syma->name && !symb->name)
- return TRUE;
-
- if (!syma)
- return FALSE;
-
- if (!symb)
- return FALSE;
-
- return strcmp (syma->name, symb->name) == 0;
+ if (!bin_file->inode_check)
+ {
+ g_print ("warning: Inode mismatch for %s (disk: "FMT64", memory: "FMT64")\n",
+ bin_file->filename,
+ (guint64)bin_file->inode, (guint64)inode);
+
+ bin_file->inode_check = TRUE;
+ }
+
+ return FALSE;
}
-guint
-symbol_hash (const void *s)
+static const ElfSym *
+get_elf_sym (bin_file_t *file,
+ const bin_symbol_t *symbol,
+ ElfParser **elf_ret)
{
- const Symbol *symbol = s;
+ GList *list;
- if (!s)
- return 0;
+ for (list = file->elf_files; list != NULL; list = list->next)
+ {
+ const ElfSym *sym = (const ElfSym *)symbol;
+ ElfParser *elf = list->data;
+
+ if (elf_parser_owns_symbol (elf, sym))
+ {
+ *elf_ret = elf;
+ return sym;
+ }
+ }
- return symbol->name? g_str_hash (symbol->name) : 0 + symbol->address;
-}
-
-void
-symbol_free (Symbol *symbol)
-{
- if (symbol->name)
- g_free (symbol->name);
- g_free (symbol);
+ g_critical ("Internal error: unrecognized symbol pointer");
+
+ *elf_ret = NULL;
+ return NULL;
}
-static void
-bfd_nonfatal (const char *string)
+const char *
+bin_symbol_get_name (bin_file_t *file,
+ const bin_symbol_t *symbol)
{
- const char *errmsg = bfd_errmsg (bfd_get_error ());
-
- if (string)
+ if (file->undefined_name == (char *)symbol)
{
- fprintf (stderr, "%s: %s: %s\n",
- g_get_application_name(), string, errmsg);
+ return file->undefined_name;
}
else
{
- fprintf (stderr, "%s: %s\n",
- g_get_application_name(), errmsg);
+ ElfParser *elf;
+ const ElfSym *sym;
+
+ sym = get_elf_sym (file, symbol, &elf);
+
+ return elf_parser_get_sym_name (elf, sym);
}
}
-static void
-bfd_fatal (const char *string)
+gulong
+bin_symbol_get_address (bin_file_t *file,
+ const bin_symbol_t *symbol)
{
- bfd_nonfatal (string);
- exit (1);
+ if (file->undefined_name == (char *)symbol)
+ {
+ return 0x0;
+ }
+ else
+ {
+ ElfParser *elf;
+ const ElfSym *sym;
+
+ sym = get_elf_sym (file, symbol, &elf);
+
+ return elf_parser_get_sym_address (elf, sym);
+ }
}
diff --git a/binfile.h b/binfile.h
index f26b15b..03c3a41 100644
--- a/binfile.h
+++ b/binfile.h
@@ -25,28 +25,22 @@
#define BIN_FILE_H
#include <glib.h>
+#include <sys/types.h>
-typedef struct BinFile BinFile;
-typedef struct Symbol Symbol;
+typedef struct bin_file_t bin_file_t;
+typedef struct bin_symbol_t bin_symbol_t;
/* Binary File */
-BinFile * bin_file_new (const char *filename);
-void bin_file_free (BinFile *bin_file);
-const Symbol *bin_file_lookup_symbol (BinFile *bin_file,
- gulong address);
-
-/* Symbol */
-struct Symbol
-{
- char * name;
- gulong address;
-};
-
-Symbol * symbol_copy (const Symbol *orig);
-gboolean symbol_equal (const void *syma,
- const void *symb);
-guint symbol_hash (const void *sym);
-void symbol_free (Symbol *symbol);
+bin_file_t * bin_file_new (const char *filename);
+void bin_file_free (bin_file_t *bin_file);
+const bin_symbol_t *bin_file_lookup_symbol (bin_file_t *bin_file,
+ gulong address);
+gboolean bin_file_check_inode (bin_file_t *bin_file,
+ ino_t inode);
+const char * bin_symbol_get_name (bin_file_t *bin_file,
+ const bin_symbol_t *symbol);
+gulong bin_symbol_get_address (bin_file_t *bin_file,
+ const bin_symbol_t *symbol);
#endif
diff --git a/collector.c b/collector.c
new file mode 100644
index 0000000..86d96ed
--- /dev/null
+++ b/collector.c
@@ -0,0 +1,804 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004, Red Hat, Inc.
+ * Copyright 2004, 2005, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdint.h>
+#include <glib.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "stackstash.h"
+#include "collector.h"
+#include "watch.h"
+#include "elfparser.h"
+#include "tracker.h"
+
+#include "perf_counter.h"
+#include "util.h"
+
+#define d_print(...)
+
+#define N_PAGES 32 /* Number of pages in the ringbuffer */
+
+#define N_WAKEUP_EVENTS 149
+
+typedef struct counter_t counter_t;
+typedef struct sample_event_t sample_event_t;
+typedef struct mmap_event_t mmap_event_t;
+typedef struct comm_event_t comm_event_t;
+typedef struct exit_event_t exit_event_t;
+typedef struct fork_event_t fork_event_t;
+typedef union counter_event_t counter_event_t;
+
+static void process_event (Collector *collector,
+ counter_t *counter,
+ counter_event_t *event);
+
+struct counter_t
+{
+ Collector * collector;
+
+ int fd;
+ struct perf_counter_mmap_page * mmap_page;
+ uint8_t * data;
+
+ uint64_t tail;
+ int cpu;
+};
+
+struct sample_event_t
+{
+ struct perf_event_header header;
+ uint64_t ip;
+ uint32_t pid, tid;
+ uint64_t n_ips;
+ uint64_t ips[1];
+};
+
+struct comm_event_t
+{
+ struct perf_event_header header;
+ uint32_t pid, tid;
+ char comm[1];
+};
+
+struct mmap_event_t
+{
+ struct perf_event_header header;
+
+ uint32_t pid, tid;
+ uint64_t addr;
+ uint64_t len;
+ uint64_t pgoff;
+ char filename[1];
+};
+
+struct fork_event_t
+{
+ struct perf_event_header header;
+
+ uint32_t pid, ppid;
+ uint32_t tid, ptid;
+};
+
+struct exit_event_t
+{
+ struct perf_event_header header;
+
+ uint32_t pid, ppid;
+ uint32_t tid, ptid;
+};
+
+union counter_event_t
+{
+ struct perf_event_header header;
+ mmap_event_t mmap;
+ comm_event_t comm;
+ sample_event_t sample;
+ fork_event_t fork;
+ exit_event_t exit;
+};
+
+struct Collector
+{
+ CollectorFunc callback;
+ gpointer data;
+
+ tracker_t * tracker;
+ GTimeVal latest_reset;
+
+ int prev_samples;
+ int n_samples;
+
+ GList * counters;
+
+ gboolean use_hw_counters;
+};
+
+static int
+get_n_cpus (void)
+{
+ return sysconf (_SC_NPROCESSORS_ONLN);
+}
+
+static int
+sysprof_perf_counter_open (struct perf_counter_attr *attr,
+ pid_t pid,
+ int cpu,
+ int group_fd,
+ unsigned long flags)
+{
+#ifndef __NR_perf_counter_open
+#if defined(__i386__)
+#define __NR_perf_counter_open 336
+#elif defined(__x86_64__)
+#define __NR_perf_counter_open 298
+#elif defined(__arm__)
+#define __NR_perf_counter_open 364
+#elif defined(__bfin__)
+#define __NR_perf_counter_open 369
+#elif defined(__frv__)
+#define __NR_perf_counter_open 336
+#elif defined(__m68k__)
+#define __NR_perf_counter_open 332
+#elif defined(__MICROBLAZE__)
+#define __NR_perf_counter_open 366
+#elif defined(__mips__) && defined(_ABIO32)
+#define __NR_perf_counter_open 4333
+#elif defined(__mips__) && defined(_ABIN32)
+#define __NR_perf_counter_open 6296
+#elif defined(__mips__) && defined(_ABI64)
+#define __NR_perf_counter_open 5292
+#elif defined(__mn10300__)
+#define __NR_perf_counter_open 337
+#elif defined(__hppa__)
+#define __NR_perf_counter_open 318
+#elif defined(__ppc__) || defined(__ppc64__)
+#define __NR_perf_counter_open 319
+#elif defined(__s390__)
+#define __NR_perf_counter_open 331
+#elif defined(__sh__) && (!defined(__SH5__) || __SH5__ == 32)
+#define __NR_perf_counter_open 336
+#elif defined(__sh__) && defined(__SH5__) && __SH5__ == 64
+#define __NR_perf_counter_open 364
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __NR_perf_counter_open 327
+#endif
+#endif
+
+ attr->size = sizeof(*attr);
+
+ return syscall (__NR_perf_counter_open, attr, pid, cpu, group_fd, flags);
+}
+
+
+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 gboolean
+in_dead_period (Collector *collector)
+{
+ GTimeVal now;
+ double diff;
+
+ g_get_current_time (&now);
+
+ diff = time_diff (&now, &collector->latest_reset);
+
+ if (diff >= 0.0 && diff < RESET_DEAD_PERIOD)
+ return TRUE;
+
+ return FALSE;
+}
+
+static int
+get_page_size (void)
+{
+ static int page_size;
+ static gboolean has_page_size = FALSE;
+
+ if (!has_page_size)
+ {
+ page_size = getpagesize();
+ has_page_size = TRUE;
+ }
+
+ return page_size;
+}
+
+static void
+on_read (gpointer data)
+{
+ counter_t *counter = data;
+ int mask = (N_PAGES * get_page_size() - 1);
+ int n_bytes = mask + 1;
+ gboolean skip_samples;
+ Collector *collector;
+ uint64_t head, tail;
+
+ collector = counter->collector;
+
+ tail = counter->tail;
+
+ head = counter->mmap_page->data_head;
+ rmb();
+
+ if (head < tail)
+ {
+ g_warning ("sysprof fails at ring buffers (head "FMT64", tail "FMT64"\n", head, tail);
+
+ tail = head;
+ }
+
+#if 0
+ /* Verify that the double mapping works */
+ x = g_random_int() & mask;
+ g_assert (*(counter->data + x) == *(counter->data + x + n_bytes));
+#endif
+
+ skip_samples = in_dead_period (collector);
+
+#if 0
+ g_print ("n bytes %d\n", head - tail);
+#endif
+
+ while (head - tail >= sizeof (struct perf_event_header))
+ {
+ struct perf_event_header *header;
+ guint8 buffer[4096];
+ guint8 *free_me;
+
+ free_me = NULL;
+
+ /* Note that:
+ *
+ * - perf events are a multiple of 64 bits
+ * - the perf event header is 64 bits
+ * - the data area is a multiple of 64 bits
+ *
+ * which means there will always be space for one header, which means we
+ * can safely dereference the size field.
+ */
+ header = (struct perf_event_header *)(counter->data + (tail & mask));
+
+ if (header->size > head - tail)
+ {
+ /* The kernel did not generate a complete event.
+ * I don't think that can happen, but we may as well
+ * be paranoid.
+ */
+ break;
+ }
+
+ if (counter->data + (tail & mask) + header->size > counter->data + n_bytes)
+ {
+ int n_before, n_after;
+ guint8 *b;
+
+ if (header->size > sizeof (buffer))
+ free_me = b = g_malloc (header->size);
+ else
+ b = buffer;
+
+ n_after = (tail & mask) + header->size - n_bytes;
+ n_before = header->size - n_after;
+
+ memcpy (b, counter->data + (tail & mask), n_before);
+ memcpy (b + n_before, counter->data, n_after);
+
+ header = (struct perf_event_header *)b;
+ }
+
+ if (!skip_samples || header->type != PERF_EVENT_SAMPLE)
+ {
+ if (header->type == PERF_EVENT_SAMPLE)
+ collector->n_samples++;
+
+ process_event (collector, counter, (counter_event_t *)header);
+ }
+
+ if (free_me)
+ g_free (free_me);
+
+ tail += header->size;
+ }
+
+ counter->tail = tail;
+ counter->mmap_page->data_tail = tail;
+
+ if (collector->callback)
+ {
+ if (collector->n_samples - collector->prev_samples >= N_WAKEUP_EVENTS)
+ {
+ gboolean first_sample = collector->prev_samples == 0;
+
+ collector->callback (first_sample, collector->data);
+
+ collector->prev_samples = collector->n_samples;
+ }
+ }
+}
+
+static void *
+fail (GError **err, const char *what)
+{
+ g_set_error (err, COLLECTOR_ERROR, COLLECTOR_ERROR_FAILED,
+ "%s: %s", what, g_strerror (errno));
+
+ return NULL;
+}
+
+static void *
+map_buffer (counter_t *counter, GError **err)
+{
+ int n_bytes = N_PAGES * get_page_size();
+ void *address;
+
+ address = mmap (NULL, n_bytes + get_page_size(), PROT_READ | PROT_WRITE, MAP_SHARED, counter->fd, 0);
+
+ if (address == MAP_FAILED)
+ return fail (err, "mmap");
+
+ return address;
+}
+
+static gboolean
+counter_set_output (counter_t *counter, int output)
+{
+ return ioctl (counter->fd, PERF_COUNTER_IOC_SET_OUTPUT, output) == 0;
+}
+
+static void
+counter_enable (counter_t *counter)
+{
+ ioctl (counter->fd, PERF_COUNTER_IOC_ENABLE);
+}
+
+static void
+counter_disable (counter_t *counter)
+{
+ d_print ("disable\n");
+
+ ioctl (counter->fd, PERF_COUNTER_IOC_DISABLE);
+}
+
+static counter_t *
+counter_new (Collector *collector,
+ int cpu,
+ counter_t *output,
+ GError **err)
+{
+ struct perf_counter_attr attr;
+ counter_t *counter;
+ int fd;
+
+ counter = g_new (counter_t, 1);
+
+ memset (&attr, 0, sizeof (attr));
+
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+ attr.sample_period = 1200000 ; /* In number of clock cycles -
+ * FIXME: consider using frequency instead
+ */
+ attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_CALLCHAIN;
+ attr.wakeup_events = N_WAKEUP_EVENTS;
+ attr.disabled = TRUE;
+
+ attr.mmap = 1;
+ attr.comm = 1;
+ attr.task = 1;
+ attr.exclude_idle = 1;
+
+ if (!collector->use_hw_counters || (fd = sysprof_perf_counter_open (&attr, -1, cpu, -1, 0)) < 0)
+ {
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.config = PERF_COUNT_SW_CPU_CLOCK;
+ attr.sample_period = 1000000;
+
+ fd = sysprof_perf_counter_open (&attr, -1, cpu, -1, 0);
+ }
+
+ if (fd < 0)
+ return fail (err, "Could not open performance counter");
+
+ counter->collector = collector;
+ counter->fd = fd;
+ counter->cpu = cpu;
+
+ if (output && counter_set_output (counter, output->fd))
+ {
+ counter->mmap_page = NULL;
+ counter->data = NULL;
+ counter->tail = 0;
+ }
+ else
+ {
+ counter->mmap_page = map_buffer (counter, err);
+
+ if (!counter->mmap_page || counter->mmap_page == MAP_FAILED)
+ return NULL;
+
+ counter->data = (uint8_t *)counter->mmap_page + get_page_size ();
+ counter->tail = 0;
+
+ fd_add_watch (fd, counter);
+
+ fd_set_read_callback (fd, on_read);
+ }
+
+ return counter;
+}
+
+static void
+counter_free (counter_t *counter)
+{
+ d_print ("munmap\n");
+
+ munmap (counter->mmap_page, (N_PAGES + 1) * get_page_size());
+ fd_remove_watch (counter->fd);
+
+ close (counter->fd);
+
+ g_free (counter);
+}
+
+/*
+ * Collector
+ */
+static void
+enable_counters (Collector *collector)
+{
+ GList *list;
+
+ d_print ("enable\n");
+
+ for (list = collector->counters; list != NULL; list = list->next)
+ {
+ counter_t *counter = list->data;
+
+ counter_enable (counter);
+ }
+}
+
+static void
+disable_counters (Collector *collector)
+{
+ GList *list;
+
+ d_print ("disable\n");
+
+ for (list = collector->counters; list != NULL; list = list->next)
+ {
+ counter_t *counter = list->data;
+
+ counter_disable (counter);
+ }
+}
+
+void
+collector_reset (Collector *collector)
+{
+ /* Disable the counters so that we won't track
+ * the activity of tracker_free()/tracker_new()
+ *
+ * They will still record fork/mmap/etc. so
+ * we can keep an accurate log of process creation
+ */
+ if (collector->counters)
+ {
+ d_print ("disable counters\n");
+
+ disable_counters (collector);
+ }
+
+ if (collector->tracker)
+ {
+ tracker_free (collector->tracker);
+ collector->tracker = tracker_new ();
+ }
+
+ collector->n_samples = 0;
+ collector->prev_samples = 0;
+
+ g_get_current_time (&collector->latest_reset);
+
+ if (collector->counters)
+ {
+ d_print ("enable counters\n");
+
+ enable_counters (collector);
+ }
+}
+
+/* callback is called whenever a new sample arrives */
+Collector *
+collector_new (gboolean use_hw_counters,
+ CollectorFunc callback,
+ gpointer data)
+{
+ Collector *collector = g_new0 (Collector, 1);
+
+ collector->callback = callback;
+ collector->data = data;
+ collector->tracker = NULL;
+ collector->use_hw_counters = use_hw_counters;
+
+ collector_reset (collector);
+
+ return collector;
+}
+
+static void
+process_mmap (Collector *collector, mmap_event_t *mmap)
+{
+ tracker_add_map (collector->tracker,
+ mmap->pid,
+ mmap->addr,
+ mmap->addr + mmap->len,
+ mmap->pgoff,
+ 0, /* inode */
+ mmap->filename);
+}
+
+static void
+process_comm (Collector *collector, comm_event_t *comm)
+{
+ d_print ("pid, tid: %d %d", comm->pid, comm->tid);
+
+ tracker_add_process (collector->tracker,
+ comm->pid,
+ comm->comm);
+}
+
+static void
+process_fork (Collector *collector, fork_event_t *fork)
+{
+ d_print ("ppid: %d pid: %d ptid: %d tid %d\n",
+ fork->ppid, fork->pid, fork->ptid, fork->tid);
+
+ tracker_add_fork (collector->tracker, fork->ppid, fork->pid);
+}
+
+static void
+process_exit (Collector *collector, exit_event_t *exit)
+{
+ d_print ("for %d %d", exit->pid, exit->tid);
+
+ tracker_add_exit (collector->tracker, exit->pid);
+}
+
+static void
+process_sample (Collector *collector,
+ sample_event_t *sample)
+{
+ uint64_t *ips;
+ int n_ips;
+
+ d_print ("pid, tid: %d %d", sample->pid, sample->tid);
+
+ if (sample->n_ips == 0)
+ {
+ uint64_t trace[3];
+
+ if (sample->header.misc & PERF_EVENT_MISC_KERNEL)
+ {
+ trace[0] = PERF_CONTEXT_KERNEL;
+ trace[1] = sample->ip;
+ trace[2] = PERF_CONTEXT_USER;
+
+ ips = trace;
+ n_ips = 3;
+ }
+ else
+ {
+ trace[0] = PERF_CONTEXT_USER;
+ trace[1] = sample->ip;
+
+ ips = trace;
+ n_ips = 2;
+ }
+ }
+ else
+ {
+ ips = sample->ips;
+ n_ips = sample->n_ips;
+ }
+
+ tracker_add_sample (collector->tracker,
+ sample->pid, ips, n_ips);
+}
+
+static void
+process_event (Collector *collector,
+ counter_t *counter,
+ counter_event_t *event)
+{
+ char *name;
+
+ switch (event->header.type)
+ {
+ case PERF_EVENT_MMAP: name = "mmap"; break;
+ case PERF_EVENT_LOST: name = "lost"; break;
+ case PERF_EVENT_COMM: name = "comm"; break;
+ case PERF_EVENT_EXIT: name = "exit"; break;
+ case PERF_EVENT_THROTTLE: name = "throttle"; break;
+ case PERF_EVENT_UNTHROTTLE: name = "unthrottle"; break;
+ case PERF_EVENT_FORK: name = "fork"; break;
+ case PERF_EVENT_READ: name = "read"; break;
+ case PERF_EVENT_SAMPLE: name = "samp"; break;
+ default: name = "unknown"; break;
+ }
+
+ d_print ("cpu %d :: %s :: ", counter->cpu, name);
+
+ switch (event->header.type)
+ {
+ case PERF_EVENT_MMAP:
+ process_mmap (collector, &event->mmap);
+ break;
+
+ case PERF_EVENT_LOST:
+ g_print ("lost event\n");
+ break;
+
+ case PERF_EVENT_COMM:
+ process_comm (collector, &event->comm);
+ break;
+
+ case PERF_EVENT_EXIT:
+ process_exit (collector, &event->exit);
+ break;
+
+ case PERF_EVENT_THROTTLE:
+ g_print ("throttle\n");
+ break;
+
+ case PERF_EVENT_UNTHROTTLE:
+ g_print ("unthrottle\n");
+ break;
+
+ case PERF_EVENT_FORK:
+ process_fork (collector, &event->fork);
+ break;
+
+ case PERF_EVENT_READ:
+ break;
+
+ case PERF_EVENT_SAMPLE:
+ process_sample (collector, &event->sample);
+ break;
+
+ default:
+ g_warning ("unknown event: %d (%d)\n",
+ event->header.type, event->header.size);
+ break;
+ }
+
+ d_print ("\n");
+}
+
+gboolean
+collector_start (Collector *collector,
+ GError **err)
+{
+ int n_cpus = get_n_cpus ();
+ int i;
+ counter_t *output;
+
+ if (!collector->tracker)
+ collector->tracker = tracker_new ();
+
+ output = NULL;
+ for (i = 0; i < n_cpus; ++i)
+ {
+ counter_t *counter = counter_new (collector, i, output, err);
+
+ if (!counter)
+ {
+ GList *list;
+
+ for (list = collector->counters; list != NULL; list = list->next)
+ counter_free (list->data);
+
+ collector->tracker = NULL;
+
+ return FALSE;
+ }
+
+ collector->counters = g_list_append (collector->counters, counter);
+
+ if (!output)
+ output = counter;
+ }
+
+ enable_counters (collector);
+
+ return TRUE;
+}
+
+void
+collector_stop (Collector *collector)
+{
+ GList *list;
+
+ if (!collector->counters)
+ return;
+
+ /* Read any remaining data */
+ for (list = collector->counters; list != NULL; list = list->next)
+ {
+ counter_t *counter = list->data;
+
+ if (counter->data)
+ on_read (counter);
+
+ counter_free (counter);
+ }
+
+ g_list_free (collector->counters);
+ collector->counters = NULL;
+}
+
+int
+collector_get_n_samples (Collector *collector)
+{
+ return collector->n_samples;
+}
+
+Profile *
+collector_create_profile (Collector *collector)
+{
+ /* The collector must be stopped when you create a profile */
+ g_assert (!collector->counters);
+
+ return tracker_create_profile (collector->tracker);
+}
+
+GQuark
+collector_error_quark (void)
+{
+ static GQuark q = 0;
+
+ if (q == 0)
+ q = g_quark_from_static_string ("collector-error-quark");
+
+ return q;
+}
diff --git a/module/sysprof-module.h b/collector.h
index 66a11ae..3c0c118 100644
--- a/module/sysprof-module.h
+++ b/collector.h
@@ -17,21 +17,29 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#ifndef SYSPROF_MODULE_H
-#define SYSPROF_MODULE_H
+#include "profile.h"
-typedef struct SysprofStackTrace SysprofStackTrace;
+typedef struct Collector Collector;
-#define SYSPROF_MAX_ADDRESSES 512
+typedef void (* CollectorFunc) (gboolean first_sample,
+ gpointer data);
-struct SysprofStackTrace
+#define COLLECTOR_ERROR collector_error_quark ()
+
+GQuark collector_error_quark (void);
+
+typedef enum
{
- int pid; /* -1 if in kernel */
- int truncated;
- int n_addresses; /* note: this can be 1 if the process was compiled
- * with -fomit-frame-pointer or is otherwise weird
- */
- void *addresses[SYSPROF_MAX_ADDRESSES];
-};
+ COLLECTOR_ERROR_FAILED
+} CollectorError;
-#endif
+/* callback is called whenever a new sample arrives */
+Collector *collector_new (gboolean use_hw_counters,
+ 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);
diff --git a/config.h.in b/config.h.in
index 0202b14..6c8fb08 100644
--- a/config.h.in
+++ b/config.h.in
@@ -3,9 +3,6 @@
/* Look for global separate debug info in this path */
#undef DEBUGDIR
-/* Define to 1 if you have the `iberty' library (-liberty). */
-#undef HAVE_LIBIBERTY
-
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
diff --git a/configure b/configure
index 96f46d0..87a6789 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.63 for sysprof 1.0.12.
+# Generated by GNU Autoconf 2.63 for sysprof 1.1.6.
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -594,16 +594,20 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='sysprof'
PACKAGE_TARNAME='sysprof'
-PACKAGE_VERSION='1.0.12'
-PACKAGE_STRING='sysprof 1.0.12'
+PACKAGE_VERSION='1.1.6'
+PACKAGE_STRING='sysprof 1.1.6'
PACKAGE_BUGREPORT=''
ac_unique_file="sysprof.glade"
ac_subst_vars='LTLIBOBJS
LIBOBJS
MODULE_SUBDIR
-DEP_LIBS
-DEP_CFLAGS
+BUILD_GUI_FALSE
+BUILD_GUI_TRUE
+GUI_DEP_LIBS
+GUI_DEP_CFLAGS
+CORE_DEP_LIBS
+CORE_DEP_CFLAGS
PKG_CONFIG
am__fastdepCC_FALSE
am__fastdepCC_TRUE
@@ -681,7 +685,6 @@ ac_user_opts='
enable_option_checking
enable_dependency_tracking
with_separate_debug_dir
-enable_kernel_module
'
ac_precious_vars='build_alias
host_alias
@@ -692,8 +695,10 @@ LDFLAGS
LIBS
CPPFLAGS
PKG_CONFIG
-DEP_CFLAGS
-DEP_LIBS'
+CORE_DEP_CFLAGS
+CORE_DEP_LIBS
+GUI_DEP_CFLAGS
+GUI_DEP_LIBS'
# Initialize some variables set by options.
@@ -1246,7 +1251,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures sysprof 1.0.12 to adapt to many kinds of systems.
+\`configure' configures sysprof 1.1.6 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1312,7 +1317,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sysprof 1.0.12:";;
+ short | recursive ) echo "Configuration of sysprof 1.1.6:";;
esac
cat <<\_ACEOF
@@ -1322,7 +1327,6 @@ Optional Features:
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--disable-dependency-tracking Speeds up one-time builds
--enable-dependency-tracking Do not reject slow dependency extractors
- --disable-kernel-module disable building kernel module
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1338,8 +1342,14 @@ Some influential environment variables:
CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
PKG_CONFIG path to pkg-config utility
- DEP_CFLAGS C compiler flags for DEP, overriding pkg-config
- DEP_LIBS linker flags for DEP, overriding pkg-config
+ CORE_DEP_CFLAGS
+ C compiler flags for CORE_DEP, overriding pkg-config
+ CORE_DEP_LIBS
+ linker flags for CORE_DEP, overriding pkg-config
+ GUI_DEP_CFLAGS
+ C compiler flags for GUI_DEP, overriding pkg-config
+ GUI_DEP_LIBS
+ linker flags for GUI_DEP, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1406,7 +1416,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sysprof configure 1.0.12
+sysprof configure 1.1.6
generated by GNU Autoconf 2.63
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1420,7 +1430,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by sysprof $as_me 1.0.12, which was
+It was created by sysprof $as_me 1.1.6, which was
generated by GNU Autoconf 2.63. Invocation command line was
$ $0 $@
@@ -2086,7 +2096,7 @@ fi
# Define the identity of the package.
PACKAGE='sysprof'
- VERSION='1.0.12'
+ VERSION='1.1.6'
# Some tools Automake needs.
@@ -3413,6 +3423,11 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+# Support silent build rules, requires at least automake-1.11. Disable
+# by either passing --disable-silent-rules to configure or passing V=1
+# to make
+
+
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
@@ -3446,45 +3461,15 @@ _ACEOF
-# Check whether --enable-kernel-module was given.
-if test "${enable_kernel_module+set}" = set; then
- enableval=$enable_kernel_module;
-fi
-
-
-kernel_module="yes"
-if test "x$enableval" = "xno"; then
- kernel_module="no"
-fi
-
-if test $kernel_module = "yes"; then
- # Activate build in module/ subdir
- MODULE_SUBDIR=module
-
- # Kernel version
- KMAJOR=`uname -r | cut -d"." -f 1`
- KMINOR=`uname -r | cut -d"." -f 2`
- KMICRO=`uname -r | cut -d"." -f 3 | cut -d"-" -f 1`
-
- if [ $KMICRO -lt 11 ] ; then
- echo \*
- echo \* Linux \>= 2.6.11 is required
- echo \*
- exit 1
- fi
-
- if ! test -f /lib/modules/`uname -r`/build/Makefile ; then
- echo \*
- echo \* Sysprof requires the kernel source code to be installed.
- echo \* On a Fedora Core system the relevant package is kernel-devel
- echo \*
- exit 1
- fi
-fi
+# Kernel version
+KMAJOR=`uname -r | cut -d"." -f 1`
+KMINOR=`uname -r | cut -d"." -f 2`
+KMICRO=`uname -r | cut -d"." -f 3 | cut -d"-" -f 1`
# Pkgconfig dependencies
-dep_modules="gtk+-2.0 > 2.6.0 gthread-2.0 gdk-pixbuf-2.0 pangoft2 libglade-2.0"
+core_dep="glib-2.0 >= 2.6.0"
+gui_dep="gtk+-2.0 > 2.6.0 gdk-pixbuf-2.0 pangoft2 libglade-2.0"
@@ -3604,35 +3589,35 @@ $as_echo "no" >&6; }
fi
pkg_failed=no
-{ $as_echo "$as_me:$LINENO: checking for DEP" >&5
-$as_echo_n "checking for DEP... " >&6; }
+{ $as_echo "$as_me:$LINENO: checking for CORE_DEP" >&5
+$as_echo_n "checking for CORE_DEP... " >&6; }
-if test -n "$DEP_CFLAGS"; then
- pkg_cv_DEP_CFLAGS="$DEP_CFLAGS"
+if test -n "$CORE_DEP_CFLAGS"; then
+ pkg_cv_CORE_DEP_CFLAGS="$CORE_DEP_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\$dep_modules\"") >&5
- ($PKG_CONFIG --exists --print-errors "$dep_modules") 2>&5
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\$core_dep\"") >&5
+ ($PKG_CONFIG --exists --print-errors "$core_dep") 2>&5
ac_status=$?
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
- pkg_cv_DEP_CFLAGS=`$PKG_CONFIG --cflags "$dep_modules" 2>/dev/null`
+ pkg_cv_CORE_DEP_CFLAGS=`$PKG_CONFIG --cflags "$core_dep" 2>/dev/null`
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
-if test -n "$DEP_LIBS"; then
- pkg_cv_DEP_LIBS="$DEP_LIBS"
+if test -n "$CORE_DEP_LIBS"; then
+ pkg_cv_CORE_DEP_LIBS="$CORE_DEP_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
- { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\$dep_modules\"") >&5
- ($PKG_CONFIG --exists --print-errors "$dep_modules") 2>&5
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\$core_dep\"") >&5
+ ($PKG_CONFIG --exists --print-errors "$core_dep") 2>&5
ac_status=$?
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
- pkg_cv_DEP_LIBS=`$PKG_CONFIG --libs "$dep_modules" 2>/dev/null`
+ pkg_cv_CORE_DEP_LIBS=`$PKG_CONFIG --libs "$core_dep" 2>/dev/null`
else
pkg_failed=yes
fi
@@ -3650,12 +3635,12 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
- DEP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$dep_modules" 2>&1`
+ CORE_DEP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$core_dep" 2>&1`
else
- DEP_PKG_ERRORS=`$PKG_CONFIG --print-errors "$dep_modules" 2>&1`
+ CORE_DEP_PKG_ERRORS=`$PKG_CONFIG --print-errors "$core_dep" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
- echo "$DEP_PKG_ERRORS" >&5
+ echo "$CORE_DEP_PKG_ERRORS" >&5
{ $as_echo "$as_me:$LINENO: result: no" >&5
$as_echo "no" >&6; }
@@ -3667,246 +3652,99 @@ elif test $pkg_failed = untried; then
$as_echo "$as_me: error: sysprof dependencies not satisfied" >&2;}
{ (exit 1); exit 1; }; }
else
- DEP_CFLAGS=$pkg_cv_DEP_CFLAGS
- DEP_LIBS=$pkg_cv_DEP_LIBS
+ CORE_DEP_CFLAGS=$pkg_cv_CORE_DEP_CFLAGS
+ CORE_DEP_LIBS=$pkg_cv_CORE_DEP_LIBS
{ $as_echo "$as_me:$LINENO: result: yes" >&5
$as_echo "yes" >&6; }
:
fi
-# libiberty and libbfd
+build_gui=yes
-{ $as_echo "$as_me:$LINENO: checking for cplus_demangle in -liberty" >&5
-$as_echo_n "checking for cplus_demangle in -liberty... " >&6; }
-if test "${ac_cv_lib_iberty_cplus_demangle+set}" = set; then
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-liberty $LIBS"
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h. */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h. */
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GUI_DEP" >&5
+$as_echo_n "checking for GUI_DEP... " >&6; }
-/* Override any GCC internal prototype to avoid an error.
- Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
-char cplus_demangle ();
-int
-main ()
-{
-return cplus_demangle ();
- ;
- return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
-$as_echo "$ac_try_echo") >&5
- (eval "$ac_link") 2>conftest.er1
+if test -n "$GUI_DEP_CFLAGS"; then
+ pkg_cv_GUI_DEP_CFLAGS="$GUI_DEP_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\$gui_dep\"") >&5
+ ($PKG_CONFIG --exists --print-errors "$gui_dep") 2>&5
ac_status=$?
- grep -v '^ *+' conftest.er1 >conftest.err
- rm -f conftest.er1
- cat conftest.err >&5
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
- (exit $ac_status); } && {
- test -z "$ac_c_werror_flag" ||
- test ! -s conftest.err
- } && test -s conftest$ac_exeext && {
- test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
- }; then
- ac_cv_lib_iberty_cplus_demangle=yes
+ (exit $ac_status); }; then
+ pkg_cv_GUI_DEP_CFLAGS=`$PKG_CONFIG --cflags "$gui_dep" 2>/dev/null`
else
- $as_echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
- ac_cv_lib_iberty_cplus_demangle=no
+ pkg_failed=yes
fi
-
-rm -rf conftest.dSYM
-rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+ else
+ pkg_failed=untried
fi
-{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_iberty_cplus_demangle" >&5
-$as_echo "$ac_cv_lib_iberty_cplus_demangle" >&6; }
-if test "x$ac_cv_lib_iberty_cplus_demangle" = x""yes; then
- :
-else
-
-{ $as_echo "$as_me:$LINENO: checking for cplus_demangle_opname in -liberty" >&5
-$as_echo_n "checking for cplus_demangle_opname in -liberty... " >&6; }
-if test "${ac_cv_lib_iberty_cplus_demangle_opname+set}" = set; then
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-liberty -ldl $LIBS"
-
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h. */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h. */
-
-/* Override any GCC internal prototype to avoid an error.
- Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
-char cplus_demangle_opname ();
-int
-main ()
-{
-return cplus_demangle_opname ();
- ;
- return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
-$as_echo "$ac_try_echo") >&5
- (eval "$ac_link") 2>conftest.er1
+if test -n "$GUI_DEP_LIBS"; then
+ pkg_cv_GUI_DEP_LIBS="$GUI_DEP_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\$gui_dep\"") >&5
+ ($PKG_CONFIG --exists --print-errors "$gui_dep") 2>&5
ac_status=$?
- grep -v '^ *+' conftest.er1 >conftest.err
- rm -f conftest.er1
- cat conftest.err >&5
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
- (exit $ac_status); } && {
- test -z "$ac_c_werror_flag" ||
- test ! -s conftest.err
- } && test -s conftest$ac_exeext && {
- test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
- }; then
- ac_cv_lib_iberty_cplus_demangle_opname=yes
+ (exit $ac_status); }; then
+ pkg_cv_GUI_DEP_LIBS=`$PKG_CONFIG --libs "$gui_dep" 2>/dev/null`
else
- $as_echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
- ac_cv_lib_iberty_cplus_demangle_opname=no
+ pkg_failed=yes
fi
-
-rm -rf conftest.dSYM
-rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+ else
+ pkg_failed=untried
fi
-{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_iberty_cplus_demangle_opname" >&5
-$as_echo "$ac_cv_lib_iberty_cplus_demangle_opname" >&6; }
-if test "x$ac_cv_lib_iberty_cplus_demangle_opname" = x""yes; then
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBIBERTY 1
-_ACEOF
- LIBS="-liberty $LIBS"
-else
- { { $as_echo "$as_me:$LINENO: error: libiberty is required to compile sysprof" >&5
-$as_echo "$as_me: error: libiberty is required to compile sysprof" >&2;}
- { (exit 1); exit 1; }; }
-fi
-
-fi
+if test $pkg_failed = yes; then
-{ $as_echo "$as_me:$LINENO: checking for bfd_get_error in -lbfd" >&5
-$as_echo_n "checking for bfd_get_error in -lbfd... " >&6; }
-if test "${ac_cv_lib_bfd_bfd_get_error+set}" = set; then
- $as_echo_n "(cached) " >&6
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-lbfd -liberty $LIBS"
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h. */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h. */
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ GUI_DEP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$gui_dep" 2>&1`
+ else
+ GUI_DEP_PKG_ERRORS=`$PKG_CONFIG --print-errors "$gui_dep" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$GUI_DEP_PKG_ERRORS" >&5
-/* Override any GCC internal prototype to avoid an error.
- Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
-char bfd_get_error ();
-int
-main ()
-{
-return bfd_get_error ();
- ;
- return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
-$as_echo "$ac_try_echo") >&5
- (eval "$ac_link") 2>conftest.er1
- ac_status=$?
- grep -v '^ *+' conftest.er1 >conftest.err
- rm -f conftest.er1
- cat conftest.err >&5
- $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
- (exit $ac_status); } && {
- test -z "$ac_c_werror_flag" ||
- test ! -s conftest.err
- } && test -s conftest$ac_exeext && {
- test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
- }; then
- ac_cv_lib_bfd_bfd_get_error=yes
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ build_gui=no
+elif test $pkg_failed = untried; then
+ build_gui=no
else
- $as_echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
- ac_cv_lib_bfd_bfd_get_error=no
+ GUI_DEP_CFLAGS=$pkg_cv_GUI_DEP_CFLAGS
+ GUI_DEP_LIBS=$pkg_cv_GUI_DEP_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ :
fi
-rm -rf conftest.dSYM
-rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_bfd_bfd_get_error" >&5
-$as_echo "$ac_cv_lib_bfd_bfd_get_error" >&6; }
-if test "x$ac_cv_lib_bfd_bfd_get_error" = x""yes; then
- DEP_LIBS="$DEP_LIBS -lbfd -liberty"
+
+
+if test "$build_gui" = yes; then
+ BUILD_GUI_TRUE=
+ BUILD_GUI_FALSE='#'
else
- { { $as_echo "$as_me:$LINENO: error: libbfd is required to compile sysprof" >&5
-$as_echo "$as_me: error: libbfd is required to compile sysprof" >&2;}
- { (exit 1); exit 1; }; }
+ BUILD_GUI_TRUE='#'
+ BUILD_GUI_FALSE=
fi
-
# emit files
+
ac_config_files="$ac_config_files Makefile"
@@ -4021,6 +3859,13 @@ $as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
Usually this means the macro was only invoked conditionally." >&2;}
{ (exit 1); exit 1; }; }
fi
+if test -z "${BUILD_GUI_TRUE}" && test -z "${BUILD_GUI_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"BUILD_GUI\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"BUILD_GUI\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
: ${CONFIG_STATUS=./config.status}
ac_write_fail=0
@@ -4343,7 +4188,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by sysprof $as_me 1.0.12, which was
+This file was extended by sysprof $as_me 1.1.6, which was
generated by GNU Autoconf 2.63. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -4406,7 +4251,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_version="\\
-sysprof config.status 1.0.12
+sysprof config.status 1.1.6
configured by $0, generated by GNU Autoconf 2.63,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -5348,3 +5193,17 @@ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi
+
+if [ $KMICRO -lt 31 ] ; then
+ echo
+ echo "%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%"
+ echo "@"
+ echo "% This version of sysprof will only work with a "
+ echo "@ 2.6.31 kernel or later, but you are using 2.6.$KMICRO."
+ echo "%"
+ echo "@ You can _compile_ it against this version, but it "
+ echo "% will not work unless you install 2.6.31 or later. "
+ echo "@"
+ echo "@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@"
+ echo
+fi
diff --git a/configure.ac b/configure.ac
index 9f35b07..b0fe3a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
AC_PREREQ(2.54)
-AC_INIT([sysprof], [1.0.12])
+AC_INIT([sysprof], [1.1.6])
AC_CONFIG_SRCDIR(sysprof.glade)
AM_INIT_AUTOMAKE(no-define)
@@ -10,6 +10,11 @@ AM_CONFIG_HEADER(config.h)
AC_PROG_CC
AC_PROG_INSTALL
+# Support silent build rules, requires at least automake-1.11. Disable
+# by either passing --disable-silent-rules to configure or passing V=1
+# to make
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
changequote(,)dnl
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
@@ -44,60 +49,27 @@ AC_ARG_WITH(separate-debug-dir,
AC_DEFINE_DIR(DEBUGDIR, debugdir,
[Look for global separate debug info in this path])
-AC_ARG_ENABLE(kernel-module,
- AC_HELP_STRING(--disable-kernel-module, disable building kernel module))
-
-kernel_module="yes"
-if test "x$enableval" = "xno"; then
- kernel_module="no"
-fi
-
-if test $kernel_module = "yes"; then
- # Activate build in module/ subdir
- MODULE_SUBDIR=module
-
- # Kernel version
- KMAJOR=`uname -r | cut -d"." -f 1`
- KMINOR=`uname -r | cut -d"." -f 2`
- KMICRO=`uname -r | cut -d"." -f 3 | cut -d"-" -f 1`
-
- if [[ $KMICRO -lt 11 ]] ; then
- echo \*
- echo \* Linux \>= 2.6.11 is required
- echo \*
- exit 1
- fi
-
- if [ ! test -f /lib/modules/`uname -r`/build/Makefile ] ; then
- echo \*
- echo \* Sysprof requires the kernel source code to be installed.
- echo \* On a Fedora Core system the relevant package is kernel-devel
- echo \*
- exit 1
- fi
-fi
+# Kernel version
+KMAJOR=`uname -r | cut -d"." -f 1`
+KMINOR=`uname -r | cut -d"." -f 2`
+KMICRO=`uname -r | cut -d"." -f 3 | cut -d"-" -f 1`
# Pkgconfig dependencies
-
-dep_modules="gtk+-2.0 > 2.6.0 gthread-2.0 gdk-pixbuf-2.0 pangoft2 libglade-2.0"
-
-PKG_CHECK_MODULES(DEP, $dep_modules, [],
- AC_MSG_ERROR([sysprof dependencies not satisfied]))
-# libiberty and libbfd
+core_dep="glib-2.0 >= 2.6.0"
+gui_dep="gtk+-2.0 > 2.6.0 gdk-pixbuf-2.0 pangoft2 libglade-2.0"
-AC_CHECK_LIB(iberty, cplus_demangle,:,
- AC_CHECK_LIB(iberty, cplus_demangle_opname, [],
- AC_MSG_ERROR([libiberty is required to compile sysprof]), -ldl))
+PKG_CHECK_MODULES(CORE_DEP, $core_dep, [], AC_MSG_ERROR([sysprof dependencies not satisfied]))
-AC_CHECK_LIB(bfd, bfd_get_error, [DEP_LIBS="$DEP_LIBS -lbfd -liberty"],
- AC_MSG_ERROR([libbfd is required to compile sysprof]),
- -liberty)
+build_gui=yes
+PKG_CHECK_MODULES(GUI_DEP, $gui_dep, [], build_gui=no)
+AM_CONDITIONAL([BUILD_GUI], [test "$build_gui" = yes])
# emit files
-AC_SUBST(DEP_LIBS)
+AC_SUBST(CORE_DEP_LIBS)
+AC_SUBST(GUI_DEP_LIBS)
AC_SUBST(MODULE_SUBDIR)
AC_CONFIG_FILES([
@@ -105,3 +77,17 @@ Makefile
])
AC_OUTPUT
+
+if [[ $KMICRO -lt 31 ]] ; then
+ echo
+ echo "%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%"
+ echo "@"
+ echo "% This version of sysprof will only work with a "
+ echo "@ 2.6.31 kernel or later, but you are using 2.6.$KMICRO."
+ echo "%"
+ echo "@ You can _compile_ it against this version, but it "
+ echo "% will not work unless you install 2.6.31 or later. "
+ echo "@"
+ echo "@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@"
+ echo
+fi
diff --git a/demangle.c b/demangle.c
new file mode 100644
index 0000000..ac0381b
--- /dev/null
+++ b/demangle.c
@@ -0,0 +1,9595 @@
+/*
+ * This file is a concatenation of the files
+ *
+ * cp-demangle.c
+ * cp-dmeangle.h
+ * cplus-dem.c
+ * demangle.h
+ *
+ * all taken from libiberty in binutils v. 2.16. After this concatenation
+ * many calls to other functions in libiberty were replaced by calls to
+ * similar functions in glib. Also global entry points that we don't need
+ * in sysprof were made static or removed.
+ *
+ * Let's hope that no bugs are ever found in this file!
+ *
+ * Maybe someday look at what can be deleted from this file
+ *
+ * - "mini string library" can be replaced with GString
+ * - "option" parameter to cplus_demangle can be deleted
+ * - demangling is always "auto"
+ */
+
+/* Copyright notices:
+ *
+ * Demangler for g++ V3 ABI.
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Written by Ian Lance Taylor <ian@wasabisystems.com>.
+ *
+ * This file is part of the libiberty library, which is part of GCC.
+
+ Defs for interface to demanglers.
+ Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002,
+ 2003, 2004 Free Software Foundation, Inc.
+
+ Internal demangler interface for g++ V3 ABI.
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor <ian@wasabisystems.com>.
+
+ Demangler for GNU C++
+ Copyright 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+ Written by James Clark (jjc@jclark.uucp)
+ Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling
+ Modified by Satish Pai (pai@apollo.hp.com) for HP demangling
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/* This code implements a demangler for the g++ V3 ABI. The ABI is
+ described on this web page:
+ http://www.codesourcery.com/cxx-abi/abi.html#mangling
+
+ This code was written while looking at the demangler written by
+ Alex Samuel <samuel@codesourcery.com>.
+
+ This code first pulls the mangled name apart into a list of
+ components, and then walks the list generating the demangled
+ name.
+
+ This file will normally define the following functions, q.v.:
+ char *cplus_demangle_v3(const char *mangled, int options)
+ char *java_demangle_v3(const char *mangled)
+ enum gnu_v3_ctor_kinds is_gnu_v3_mangled_ctor (const char *name)
+ enum gnu_v3_dtor_kinds is_gnu_v3_mangled_dtor (const char *name)
+
+ Also, the interface to the component list is public, and defined in
+ demangle.h. The interface consists of these types, which are
+ defined in demangle.h:
+ enum demangle_component_type
+ struct demangle_component
+ and these functions defined in this file:
+ cplus_demangle_fill_name
+ cplus_demangle_fill_extended_operator
+ cplus_demangle_fill_ctor
+ cplus_demangle_fill_dtor
+ cplus_demangle_print
+ and other functions defined in the file cp-demint.c.
+
+ This file also defines some other functions and variables which are
+ only to be used by the file cp-demint.c.
+
+ Preprocessor macros you can define while compiling this file:
+
+ IN_LIBGCC2
+ If defined, this file defines the following function, q.v.:
+ char *__cxa_demangle (const char *mangled, char *buf, size_t *len,
+ int *status)
+ instead of cplus_demangle_v3() and java_demangle_v3().
+
+ IN_GLIBCPP_V3
+ If defined, this file defines only __cxa_demangle(), and no other
+ publically visible functions or variables.
+
+ CP_DEMANGLE_DEBUG
+ If defined, turns on debugging mode, which prints information on
+ stdout about the mangled string. This is not generally useful.
+*/
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+/* Defs for interface to demanglers.
+ Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002,
+ 2003, 2004 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+
+#if !defined (DEMANGLE_H)
+#define DEMANGLE_H
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Options passed to cplus_demangle (in 2nd parameter). */
+
+#define DMGL_NO_OPTS 0 /* For readability... */
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */
+#define DMGL_VERBOSE (1 << 3) /* Include implementation details. */
+#define DMGL_TYPES (1 << 4) /* Also try to demangle type encodings. */
+
+#define DMGL_AUTO (1 << 8)
+#define DMGL_GNU (1 << 9)
+#define DMGL_LUCID (1 << 10)
+#define DMGL_ARM (1 << 11)
+#define DMGL_HP (1 << 12) /* For the HP aCC compiler;
+ same as ARM except for
+ template arguments, etc. */
+#define DMGL_EDG (1 << 13)
+#define DMGL_GNU_V3 (1 << 14)
+#define DMGL_GNAT (1 << 15)
+
+/* If none of these are set, use 'current_demangling_style' as the default. */
+#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT)
+
+/* Enumeration of possible demangling styles.
+
+ Lucid and ARM styles are still kept logically distinct, even though
+ they now both behave identically. The resulting style is actual the
+ union of both. I.E. either style recognizes both "__pt__" and "__rf__"
+ for operator "->", even though the first is lucid style and the second
+ is ARM style. (FIXME?) */
+
+enum demangling_styles
+{
+ no_demangling = -1,
+ unknown_demangling = 0,
+ auto_demangling = DMGL_AUTO,
+ gnu_demangling = DMGL_GNU,
+ lucid_demangling = DMGL_LUCID,
+ arm_demangling = DMGL_ARM,
+ hp_demangling = DMGL_HP,
+ edg_demangling = DMGL_EDG,
+ gnu_v3_demangling = DMGL_GNU_V3,
+ java_demangling = DMGL_JAVA,
+ gnat_demangling = DMGL_GNAT
+};
+
+/* Define string names for the various demangling styles. */
+
+#define NO_DEMANGLING_STYLE_STRING "none"
+#define AUTO_DEMANGLING_STYLE_STRING "auto"
+#define GNU_DEMANGLING_STYLE_STRING "gnu"
+#define LUCID_DEMANGLING_STYLE_STRING "lucid"
+#define ARM_DEMANGLING_STYLE_STRING "arm"
+#define HP_DEMANGLING_STYLE_STRING "hp"
+#define EDG_DEMANGLING_STYLE_STRING "edg"
+#define GNU_V3_DEMANGLING_STYLE_STRING "gnu-v3"
+#define JAVA_DEMANGLING_STYLE_STRING "java"
+#define GNAT_DEMANGLING_STYLE_STRING "gnat"
+
+/* Some macros to test what demangling style is active. */
+
+#define CURRENT_DEMANGLING_STYLE current_demangling_style
+#define AUTO_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_AUTO)
+#define GNU_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU)
+#define LUCID_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_LUCID)
+#define ARM_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_ARM)
+#define HP_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_HP)
+#define EDG_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_EDG)
+#define GNU_V3_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU_V3)
+#define JAVA_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_JAVA)
+#define GNAT_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNAT)
+
+/* Provide information about the available demangle styles. This code is
+ pulled from gdb into libiberty because it is useful to binutils also. */
+
+#define PARAMS(a) a
+
+#define ATTRIBUTE_NORETURN G_GNUC_NORETURN
+#define ATTRIBUTE_UNUSED G_GNUC_UNUSED
+
+static const struct demangler_engine
+{
+ const char *const demangling_style_name;
+ const enum demangling_styles demangling_style;
+ const char *const demangling_style_doc;
+} libiberty_demanglers[];
+
+#if 0
+extern char *
+cplus_demangle PARAMS ((const char *mangled, int options));
+#endif
+
+#if 0
+extern int
+cplus_demangle_opname PARAMS ((const char *opname, char *result, int options));
+
+extern const char *
+cplus_mangle_opname PARAMS ((const char *opname, int options));
+#endif
+
+/* Note: This sets global state. FIXME if you care about multi-threading. */
+
+enum gnu_v3_ctor_kinds {
+ gnu_v3_complete_object_ctor = 1,
+ gnu_v3_base_object_ctor,
+ gnu_v3_complete_object_allocating_ctor
+};
+
+/* Return non-zero iff NAME is the mangled form of a constructor name
+ in the G++ V3 ABI demangling style. Specifically, return an `enum
+ gnu_v3_ctor_kinds' value indicating what kind of constructor
+ it is. */
+extern enum gnu_v3_ctor_kinds
+ is_gnu_v3_mangled_ctor PARAMS ((const char *name));
+
+
+enum gnu_v3_dtor_kinds {
+ gnu_v3_deleting_dtor = 1,
+ gnu_v3_complete_object_dtor,
+ gnu_v3_base_object_dtor
+};
+
+/* Return non-zero iff NAME is the mangled form of a destructor name
+ in the G++ V3 ABI demangling style. Specifically, return an `enum
+ gnu_v3_dtor_kinds' value, indicating what kind of destructor
+ it is. */
+extern enum gnu_v3_dtor_kinds
+ is_gnu_v3_mangled_dtor PARAMS ((const char *name));
+
+/* The V3 demangler works in two passes. The first pass builds a tree
+ representation of the mangled name, and the second pass turns the
+ tree representation into a demangled string. Here we define an
+ interface to permit a caller to build their own tree
+ representation, which they can pass to the demangler to get a
+ demangled string. This can be used to canonicalize user input into
+ something which the demangler might output. It could also be used
+ by other demanglers in the future. */
+
+/* These are the component types which may be found in the tree. Many
+ component types have one or two subtrees, referred to as left and
+ right (a component type with only one subtree puts it in the left
+ subtree). */
+
+enum demangle_component_type
+{
+ /* A name, with a length and a pointer to a string. */
+ DEMANGLE_COMPONENT_NAME,
+ /* A qualified name. The left subtree is a class or namespace or
+ some such thing, and the right subtree is a name qualified by
+ that class. */
+ DEMANGLE_COMPONENT_QUAL_NAME,
+ /* A local name. The left subtree describes a function, and the
+ right subtree is a name which is local to that function. */
+ DEMANGLE_COMPONENT_LOCAL_NAME,
+ /* A typed name. The left subtree is a name, and the right subtree
+ describes that name as a function. */
+ DEMANGLE_COMPONENT_TYPED_NAME,
+ /* A template. The left subtree is a template name, and the right
+ subtree is a template argument list. */
+ DEMANGLE_COMPONENT_TEMPLATE,
+ /* A template parameter. This holds a number, which is the template
+ parameter index. */
+ DEMANGLE_COMPONENT_TEMPLATE_PARAM,
+ /* A constructor. This holds a name and the kind of
+ constructor. */
+ DEMANGLE_COMPONENT_CTOR,
+ /* A destructor. This holds a name and the kind of destructor. */
+ DEMANGLE_COMPONENT_DTOR,
+ /* A vtable. This has one subtree, the type for which this is a
+ vtable. */
+ DEMANGLE_COMPONENT_VTABLE,
+ /* A VTT structure. This has one subtree, the type for which this
+ is a VTT. */
+ DEMANGLE_COMPONENT_VTT,
+ /* A construction vtable. The left subtree is the type for which
+ this is a vtable, and the right subtree is the derived type for
+ which this vtable is built. */
+ DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE,
+ /* A typeinfo structure. This has one subtree, the type for which
+ this is the tpeinfo structure. */
+ DEMANGLE_COMPONENT_TYPEINFO,
+ /* A typeinfo name. This has one subtree, the type for which this
+ is the typeinfo name. */
+ DEMANGLE_COMPONENT_TYPEINFO_NAME,
+ /* A typeinfo function. This has one subtree, the type for which
+ this is the tpyeinfo function. */
+ DEMANGLE_COMPONENT_TYPEINFO_FN,
+ /* A thunk. This has one subtree, the name for which this is a
+ thunk. */
+ DEMANGLE_COMPONENT_THUNK,
+ /* A virtual thunk. This has one subtree, the name for which this
+ is a virtual thunk. */
+ DEMANGLE_COMPONENT_VIRTUAL_THUNK,
+ /* A covariant thunk. This has one subtree, the name for which this
+ is a covariant thunk. */
+ DEMANGLE_COMPONENT_COVARIANT_THUNK,
+ /* A Java class. This has one subtree, the type. */
+ DEMANGLE_COMPONENT_JAVA_CLASS,
+ /* A guard variable. This has one subtree, the name for which this
+ is a guard variable. */
+ DEMANGLE_COMPONENT_GUARD,
+ /* A reference temporary. This has one subtree, the name for which
+ this is a temporary. */
+ DEMANGLE_COMPONENT_REFTEMP,
+ /* A standard substitution. This holds the name of the
+ substitution. */
+ DEMANGLE_COMPONENT_SUB_STD,
+ /* The restrict qualifier. The one subtree is the type which is
+ being qualified. */
+ DEMANGLE_COMPONENT_RESTRICT,
+ /* The volatile qualifier. The one subtree is the type which is
+ being qualified. */
+ DEMANGLE_COMPONENT_VOLATILE,
+ /* The const qualifier. The one subtree is the type which is being
+ qualified. */
+ DEMANGLE_COMPONENT_CONST,
+ /* The restrict qualifier modifying a member function. The one
+ subtree is the type which is being qualified. */
+ DEMANGLE_COMPONENT_RESTRICT_THIS,
+ /* The volatile qualifier modifying a member function. The one
+ subtree is the type which is being qualified. */
+ DEMANGLE_COMPONENT_VOLATILE_THIS,
+ /* The const qualifier modifying a member function. The one subtree
+ is the type which is being qualified. */
+ DEMANGLE_COMPONENT_CONST_THIS,
+ /* A vendor qualifier. The left subtree is the type which is being
+ qualified, and the right subtree is the name of the
+ qualifier. */
+ DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL,
+ /* A pointer. The one subtree is the type which is being pointed
+ to. */
+ DEMANGLE_COMPONENT_POINTER,
+ /* A reference. The one subtree is the type which is being
+ referenced. */
+ DEMANGLE_COMPONENT_REFERENCE,
+ /* A complex type. The one subtree is the base type. */
+ DEMANGLE_COMPONENT_COMPLEX,
+ /* An imaginary type. The one subtree is the base type. */
+ DEMANGLE_COMPONENT_IMAGINARY,
+ /* A builtin type. This holds the builtin type information. */
+ DEMANGLE_COMPONENT_BUILTIN_TYPE,
+ /* A vendor's builtin type. This holds the name of the type. */
+ DEMANGLE_COMPONENT_VENDOR_TYPE,
+ /* A function type. The left subtree is the return type. The right
+ subtree is a list of ARGLIST nodes. Either or both may be
+ NULL. */
+ DEMANGLE_COMPONENT_FUNCTION_TYPE,
+ /* An array type. The left subtree is the dimension, which may be
+ NULL, or a string (represented as DEMANGLE_COMPONENT_NAME), or an
+ expression. The right subtree is the element type. */
+ DEMANGLE_COMPONENT_ARRAY_TYPE,
+ /* A pointer to member type. The left subtree is the class type,
+ and the right subtree is the member type. CV-qualifiers appear
+ on the latter. */
+ DEMANGLE_COMPONENT_PTRMEM_TYPE,
+ /* An argument list. The left subtree is the current argument, and
+ the right subtree is either NULL or another ARGLIST node. */
+ DEMANGLE_COMPONENT_ARGLIST,
+ /* A template argument list. The left subtree is the current
+ template argument, and the right subtree is either NULL or
+ another TEMPLATE_ARGLIST node. */
+ DEMANGLE_COMPONENT_TEMPLATE_ARGLIST,
+ /* An operator. This holds information about a standard
+ operator. */
+ DEMANGLE_COMPONENT_OPERATOR,
+ /* An extended operator. This holds the number of arguments, and
+ the name of the extended operator. */
+ DEMANGLE_COMPONENT_EXTENDED_OPERATOR,
+ /* A typecast, represented as a unary operator. The one subtree is
+ the type to which the argument should be cast. */
+ DEMANGLE_COMPONENT_CAST,
+ /* A unary expression. The left subtree is the operator, and the
+ right subtree is the single argument. */
+ DEMANGLE_COMPONENT_UNARY,
+ /* A binary expression. The left subtree is the operator, and the
+ right subtree is a BINARY_ARGS. */
+ DEMANGLE_COMPONENT_BINARY,
+ /* Arguments to a binary expression. The left subtree is the first
+ argument, and the right subtree is the second argument. */
+ DEMANGLE_COMPONENT_BINARY_ARGS,
+ /* A trinary expression. The left subtree is the operator, and the
+ right subtree is a TRINARY_ARG1. */
+ DEMANGLE_COMPONENT_TRINARY,
+ /* Arguments to a trinary expression. The left subtree is the first
+ argument, and the right subtree is a TRINARY_ARG2. */
+ DEMANGLE_COMPONENT_TRINARY_ARG1,
+ /* More arguments to a trinary expression. The left subtree is the
+ second argument, and the right subtree is the third argument. */
+ DEMANGLE_COMPONENT_TRINARY_ARG2,
+ /* A literal. The left subtree is the type, and the right subtree
+ is the value, represented as a DEMANGLE_COMPONENT_NAME. */
+ DEMANGLE_COMPONENT_LITERAL,
+ /* A negative literal. Like LITERAL, but the value is negated.
+ This is a minor hack: the NAME used for LITERAL points directly
+ to the mangled string, but since negative numbers are mangled
+ using 'n' instead of '-', we want a way to indicate a negative
+ number which involves neither modifying the mangled string nor
+ allocating a new copy of the literal in memory. */
+ DEMANGLE_COMPONENT_LITERAL_NEG
+};
+
+/* Types which are only used internally. */
+
+struct demangle_operator_info;
+struct demangle_builtin_type_info;
+
+/* A node in the tree representation is an instance of a struct
+ demangle_component. Note that the field names of the struct are
+ not well protected against macros defined by the file including
+ this one. We can fix this if it ever becomes a problem. */
+
+struct demangle_component
+{
+ /* The type of this component. */
+ enum demangle_component_type type;
+
+ union
+ {
+ /* For DEMANGLE_COMPONENT_NAME. */
+ struct
+ {
+ /* A pointer to the name (which need not NULL terminated) and
+ its length. */
+ const char *s;
+ int len;
+ } s_name;
+
+ /* For DEMANGLE_COMPONENT_OPERATOR. */
+ struct
+ {
+ /* Operator. */
+ const struct demangle_operator_info *op;
+ } s_operator;
+
+ /* For DEMANGLE_COMPONENT_EXTENDED_OPERATOR. */
+ struct
+ {
+ /* Number of arguments. */
+ int args;
+ /* Name. */
+ struct demangle_component *name;
+ } s_extended_operator;
+
+ /* For DEMANGLE_COMPONENT_CTOR. */
+ struct
+ {
+ /* Kind of constructor. */
+ enum gnu_v3_ctor_kinds kind;
+ /* Name. */
+ struct demangle_component *name;
+ } s_ctor;
+
+ /* For DEMANGLE_COMPONENT_DTOR. */
+ struct
+ {
+ /* Kind of destructor. */
+ enum gnu_v3_dtor_kinds kind;
+ /* Name. */
+ struct demangle_component *name;
+ } s_dtor;
+
+ /* For DEMANGLE_COMPONENT_BUILTIN_TYPE. */
+ struct
+ {
+ /* Builtin type. */
+ const struct demangle_builtin_type_info *type;
+ } s_builtin;
+
+ /* For DEMANGLE_COMPONENT_SUB_STD. */
+ struct
+ {
+ /* Standard substitution string. */
+ const char* string;
+ /* Length of string. */
+ int len;
+ } s_string;
+
+ /* For DEMANGLE_COMPONENT_TEMPLATE_PARAM. */
+ struct
+ {
+ /* Template parameter index. */
+ long number;
+ } s_number;
+
+ /* For other types. */
+ struct
+ {
+ /* Left (or only) subtree. */
+ struct demangle_component *left;
+ /* Right subtree. */
+ struct demangle_component *right;
+ } s_binary;
+
+ } u;
+};
+
+/* People building mangled trees are expected to allocate instances of
+ struct demangle_component themselves. They can then call one of
+ the following functions to fill them in. */
+
+/* Fill in most component types with a left subtree and a right
+ subtree. Returns non-zero on success, zero on failure, such as an
+ unrecognized or inappropriate component type. */
+
+extern int
+cplus_demangle_fill_component PARAMS ((struct demangle_component *fill,
+ enum demangle_component_type,
+ struct demangle_component *left,
+ struct demangle_component *right));
+
+/* Fill in a DEMANGLE_COMPONENT_NAME. Returns non-zero on success,
+ zero for bad arguments. */
+
+extern int
+cplus_demangle_fill_name PARAMS ((struct demangle_component *fill,
+ const char *, int));
+
+/* Fill in a DEMANGLE_COMPONENT_BUILTIN_TYPE, using the name of the
+ builtin type (e.g., "int", etc.). Returns non-zero on success,
+ zero if the type is not recognized. */
+
+extern int
+cplus_demangle_fill_builtin_type PARAMS ((struct demangle_component *fill,
+ const char *type_name));
+
+/* Fill in a DEMANGLE_COMPONENT_OPERATOR, using the name of the
+ operator and the number of arguments which it takes (the latter is
+ used to disambiguate operators which can be both binary and unary,
+ such as '-'). Returns non-zero on success, zero if the operator is
+ not recognized. */
+
+extern int
+cplus_demangle_fill_operator PARAMS ((struct demangle_component *fill,
+ const char *opname, int args));
+
+/* Fill in a DEMANGLE_COMPONENT_EXTENDED_OPERATOR, providing the
+ number of arguments and the name. Returns non-zero on success,
+ zero for bad arguments. */
+
+extern int
+cplus_demangle_fill_extended_operator PARAMS ((struct demangle_component *fill,
+ int numargs,
+ struct demangle_component *nm));
+
+/* Fill in a DEMANGLE_COMPONENT_CTOR. Returns non-zero on success,
+ zero for bad arguments. */
+
+extern int
+cplus_demangle_fill_ctor PARAMS ((struct demangle_component *fill,
+ enum gnu_v3_ctor_kinds kind,
+ struct demangle_component *name));
+
+/* Fill in a DEMANGLE_COMPONENT_DTOR. Returns non-zero on success,
+ zero for bad arguments. */
+
+extern int
+cplus_demangle_fill_dtor PARAMS ((struct demangle_component *fill,
+ enum gnu_v3_dtor_kinds kind,
+ struct demangle_component *name));
+
+/* This function translates a mangled name into a struct
+ demangle_component tree. The first argument is the mangled name.
+ The second argument is DMGL_* options. This returns a pointer to a
+ tree on success, or NULL on failure. On success, the third
+ argument is set to a block of memory allocated by malloc. This
+ block should be passed to free when the tree is no longer
+ needed. */
+
+extern struct demangle_component *
+cplus_demangle_v3_components PARAMS ((const char *mangled,
+ int options,
+ void **mem));
+
+/* This function takes a struct demangle_component tree and returns
+ the corresponding demangled string. The first argument is DMGL_*
+ options. The second is the tree to demangle. The third is a guess
+ at the length of the demangled string, used to initially allocate
+ the return buffer. The fourth is a pointer to a size_t. On
+ success, this function returns a buffer allocated by malloc(), and
+ sets the size_t pointed to by the fourth argument to the size of
+ the allocated buffer (not the length of the returned string). On
+ failure, this function returns NULL, and sets the size_t pointed to
+ by the fourth argument to 0 for an invalid tree, or to 1 for a
+ memory allocation error. */
+
+extern char *
+cplus_demangle_print PARAMS ((int options,
+ const struct demangle_component *tree,
+ int estimated_length,
+ size_t *p_allocated_size));
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* DEMANGLE_H */
+
+
+/* V3 ABI demangling entry points, defined in cp-demangle.c. */
+static char* cplus_demangle_v3 PARAMS ((const char* mangled, int options));
+
+static char* java_demangle_v3 PARAMS ((const char* mangled));
+
+/* Internal demangler interface for g++ V3 ABI.
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor <ian@wasabisystems.com>.
+
+ This file is part of the libiberty library, which is part of GCC.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ In addition to the permissions in the GNU General Public License, the
+ Free Software Foundation gives you unlimited permission to link the
+ compiled version of this file into combinations with other programs,
+ and to distribute those combinations without any restriction coming
+ from the use of this file. (The General Public License restrictions
+ do apply in other respects; for example, they cover modification of
+ the file, and distribution when not linked into a combined
+ executable.)
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* This file provides some definitions shared by cp-demangle.c and
+ cp-demint.c. It should not be included by any other files. */
+
+/* Information we keep for operators. */
+
+struct demangle_operator_info
+{
+ /* Mangled name. */
+ const char *code;
+ /* Real name. */
+ const char *name;
+ /* Length of real name. */
+ int len;
+ /* Number of arguments. */
+ int args;
+};
+
+/* How to print the value of a builtin type. */
+
+enum d_builtin_type_print
+{
+ /* Print as (type)val. */
+ D_PRINT_DEFAULT,
+ /* Print as integer. */
+ D_PRINT_INT,
+ /* Print as unsigned integer, with trailing "u". */
+ D_PRINT_UNSIGNED,
+ /* Print as long, with trailing "l". */
+ D_PRINT_LONG,
+ /* Print as unsigned long, with trailing "ul". */
+ D_PRINT_UNSIGNED_LONG,
+ /* Print as long long, with trailing "ll". */
+ D_PRINT_LONG_LONG,
+ /* Print as unsigned long long, with trailing "ull". */
+ D_PRINT_UNSIGNED_LONG_LONG,
+ /* Print as bool. */
+ D_PRINT_BOOL,
+ /* Print as float--put value in square brackets. */
+ D_PRINT_FLOAT,
+ /* Print in usual way, but here to detect void. */
+ D_PRINT_VOID
+};
+
+/* Information we keep for a builtin type. */
+
+struct demangle_builtin_type_info
+{
+ /* Type name. */
+ const char *name;
+ /* Length of type name. */
+ int len;
+ /* Type name when using Java. */
+ const char *java_name;
+ /* Length of java name. */
+ int java_len;
+ /* How to print a value of this type. */
+ enum d_builtin_type_print print;
+};
+
+/* The information structure we pass around. */
+
+struct d_info
+{
+ /* The string we are demangling. */
+ const char *s;
+ /* The end of the string we are demangling. */
+ const char *send;
+ /* The options passed to the demangler. */
+ int options;
+ /* The next character in the string to consider. */
+ const char *n;
+ /* The array of components. */
+ struct demangle_component *comps;
+ /* The index of the next available component. */
+ int next_comp;
+ /* The number of available component structures. */
+ int num_comps;
+ /* The array of substitutions. */
+ struct demangle_component **subs;
+ /* The index of the next substitution. */
+ int next_sub;
+ /* The number of available entries in the subs array. */
+ int num_subs;
+ /* The number of substitutions which we actually made from the subs
+ array, plus the number of template parameter references we
+ saw. */
+ int did_subs;
+ /* The last name we saw, for constructors and destructors. */
+ struct demangle_component *last_name;
+ /* A running total of the length of large expansions from the
+ mangled name to the demangled name, such as standard
+ substitutions and builtin types. */
+ int expansion;
+};
+
+#define d_peek_char(di) (*((di)->n))
+#define d_peek_next_char(di) ((di)->n[1])
+#define d_advance(di, i) ((di)->n += (i))
+#define d_next_char(di) (*((di)->n++))
+#define d_str(di) ((di)->n)
+
+/* Functions and arrays in cp-demangle.c which are referenced by
+ functions in cp-demint.c. */
+#define CP_STATIC_IF_GLIBCPP_V3 static
+
+#define D_BUILTIN_TYPE_COUNT (26)
+
+#if 0
+static struct demangle_component *
+cplus_demangle_mangled_name PARAMS ((struct d_info *, int));
+#endif
+
+#if 0
+static struct demangle_component *
+cplus_demangle_type PARAMS ((struct d_info *));
+#endif
+
+extern void
+cplus_demangle_init_info PARAMS ((const char *, int, size_t, struct d_info *));
+
+#if 0
+/* cp-demangle.c needs to define this a little differently */
+#undef CP_STATIC_IF_GLIBCPP_V3
+#endif
+#define IN_GLIBCPP_V3
+
+/* If IN_GLIBCPP_V3 is defined, some functions are made static. We
+ also rename them via #define to avoid compiler errors when the
+ static definition conflicts with the extern declaration in a header
+ file. */
+#ifdef IN_GLIBCPP_V3
+
+#define CP_STATIC_IF_GLIBCPP_V3 static
+
+#define cplus_demangle_fill_name d_fill_name
+static int
+d_fill_name PARAMS ((struct demangle_component *, const char *, int));
+
+#define cplus_demangle_fill_extended_operator d_fill_extended_operator
+static int
+d_fill_extended_operator PARAMS ((struct demangle_component *, int,
+ struct demangle_component *));
+
+#define cplus_demangle_fill_ctor d_fill_ctor
+static int
+d_fill_ctor PARAMS ((struct demangle_component *, enum gnu_v3_ctor_kinds,
+ struct demangle_component *));
+
+#define cplus_demangle_fill_dtor d_fill_dtor
+static int
+d_fill_dtor PARAMS ((struct demangle_component *, enum gnu_v3_dtor_kinds,
+ struct demangle_component *));
+
+#define cplus_demangle_mangled_name d_mangled_name
+static struct demangle_component *
+d_mangled_name PARAMS ((struct d_info *, int));
+
+#define cplus_demangle_type d_type
+static struct demangle_component *
+d_type PARAMS ((struct d_info *));
+
+#define cplus_demangle_print d_print
+static char *
+d_print PARAMS ((int, const struct demangle_component *, int, size_t *));
+
+#define cplus_demangle_init_info d_init_info
+static void
+d_init_info PARAMS ((const char *, int, size_t, struct d_info *));
+
+#else /* ! defined(IN_GLIBCPP_V3) */
+#define CP_STATIC_IF_GLIBCPP_V3
+#endif /* ! defined(IN_GLIBCPP_V3) */
+
+/* See if the compiler supports dynamic arrays. */
+
+#ifdef __GNUC__
+#define CP_DYNAMIC_ARRAYS
+#else
+#ifdef __STDC__
+#ifdef __STDC_VERSION__
+#if __STDC_VERSION__ >= 199901L
+#define CP_DYNAMIC_ARRAYS
+#endif /* __STDC__VERSION >= 199901L */
+#endif /* defined (__STDC_VERSION__) */
+#endif /* defined (__STDC__) */
+#endif /* ! defined (__GNUC__) */
+
+/* We avoid pulling in the ctype tables, to prevent pulling in
+ additional unresolved symbols when this code is used in a library.
+ FIXME: Is this really a valid reason? This comes from the original
+ V3 demangler code.
+
+ As of this writing this file has the following undefined references
+ when compiled with -DIN_GLIBCPP_V3: malloc, realloc, free, memcpy,
+ strcpy, strcat, strlen. */
+
+#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
+#define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define IS_LOWER(c) ((c) >= 'a' && (c) <= 'z')
+
+/* The prefix prepended by GCC to an identifier represnting the
+ anonymous namespace. */
+#define ANONYMOUS_NAMESPACE_PREFIX "_GLOBAL_"
+#define ANONYMOUS_NAMESPACE_PREFIX_LEN \
+ (sizeof (ANONYMOUS_NAMESPACE_PREFIX) - 1)
+
+/* Information we keep for the standard substitutions. */
+
+struct d_standard_sub_info
+{
+ /* The code for this substitution. */
+ char code;
+ /* The simple string it expands to. */
+ const char *simple_expansion;
+ /* The length of the simple expansion. */
+ int simple_len;
+ /* The results of a full, verbose, expansion. This is used when
+ qualifying a constructor/destructor, or when in verbose mode. */
+ const char *full_expansion;
+ /* The length of the full expansion. */
+ int full_len;
+ /* What to set the last_name field of d_info to; NULL if we should
+ not set it. This is only relevant when qualifying a
+ constructor/destructor. */
+ const char *set_last_name;
+ /* The length of set_last_name. */
+ int set_last_name_len;
+};
+
+/* Accessors for subtrees of struct demangle_component. */
+
+#define d_left(dc) ((dc)->u.s_binary.left)
+#define d_right(dc) ((dc)->u.s_binary.right)
+
+/* A list of templates. This is used while printing. */
+
+struct d_print_template
+{
+ /* Next template on the list. */
+ struct d_print_template *next;
+ /* This template. */
+ const struct demangle_component *template;
+};
+
+/* A list of type modifiers. This is used while printing. */
+
+struct d_print_mod
+{
+ /* Next modifier on the list. These are in the reverse of the order
+ in which they appeared in the mangled string. */
+ struct d_print_mod *next;
+ /* The modifier. */
+ const struct demangle_component *mod;
+ /* Whether this modifier was printed. */
+ int printed;
+ /* The list of templates which applies to this modifier. */
+ struct d_print_template *templates;
+};
+
+/* We use this structure to hold information during printing. */
+
+struct d_print_info
+{
+ /* The options passed to the demangler. */
+ int options;
+ /* Buffer holding the result. */
+ char *buf;
+ /* Current length of data in buffer. */
+ size_t len;
+ /* Allocated size of buffer. */
+ size_t alc;
+ /* The current list of templates, if any. */
+ struct d_print_template *templates;
+ /* The current list of modifiers (e.g., pointer, reference, etc.),
+ if any. */
+ struct d_print_mod *modifiers;
+ /* Set to 1 if we had a memory allocation failure. */
+ int allocation_failure;
+};
+
+#define d_print_saw_error(dpi) ((dpi)->buf == NULL)
+
+#define d_append_char(dpi, c) \
+ do \
+ { \
+ if ((dpi)->buf != NULL && (dpi)->len < (dpi)->alc) \
+ (dpi)->buf[(dpi)->len++] = (c); \
+ else \
+ d_print_append_char ((dpi), (c)); \
+ } \
+ while (0)
+
+#define d_append_buffer(dpi, s, l) \
+ do \
+ { \
+ if ((dpi)->buf != NULL && (dpi)->len + (l) <= (dpi)->alc) \
+ { \
+ memcpy ((dpi)->buf + (dpi)->len, (s), (l)); \
+ (dpi)->len += l; \
+ } \
+ else \
+ d_print_append_buffer ((dpi), (s), (l)); \
+ } \
+ while (0)
+
+#define d_append_string_constant(dpi, s) \
+ d_append_buffer (dpi, (s), sizeof (s) - 1)
+
+#define d_last_char(dpi) \
+ ((dpi)->buf == NULL || (dpi)->len == 0 ? '\0' : (dpi)->buf[(dpi)->len - 1])
+
+#ifdef CP_DEMANGLE_DEBUG
+static void
+d_dump PARAMS ((struct demangle_component *, int));
+#endif
+
+static struct demangle_component *
+d_make_empty PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_make_comp PARAMS ((struct d_info *, enum demangle_component_type,
+ struct demangle_component *,
+ struct demangle_component *));
+
+static struct demangle_component *
+d_make_name PARAMS ((struct d_info *, const char *, int));
+
+static struct demangle_component *
+d_make_builtin_type PARAMS ((struct d_info *,
+ const struct demangle_builtin_type_info *));
+
+static struct demangle_component *
+d_make_operator PARAMS ((struct d_info *,
+ const struct demangle_operator_info *));
+
+static struct demangle_component *
+d_make_extended_operator PARAMS ((struct d_info *, int,
+ struct demangle_component *));
+
+static struct demangle_component *
+d_make_ctor PARAMS ((struct d_info *, enum gnu_v3_ctor_kinds,
+ struct demangle_component *));
+
+static struct demangle_component *
+d_make_dtor PARAMS ((struct d_info *, enum gnu_v3_dtor_kinds,
+ struct demangle_component *));
+
+static struct demangle_component *
+d_make_template_param PARAMS ((struct d_info *, long));
+
+static struct demangle_component *
+d_make_sub PARAMS ((struct d_info *, const char *, int));
+
+static int
+has_return_type PARAMS ((struct demangle_component *));
+
+static int
+is_ctor_dtor_or_conversion PARAMS ((struct demangle_component *));
+
+static struct demangle_component *
+d_encoding PARAMS ((struct d_info *, int));
+
+static struct demangle_component *
+d_name PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_nested_name PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_prefix PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_unqualified_name PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_source_name PARAMS ((struct d_info *));
+
+static long
+d_number PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_identifier PARAMS ((struct d_info *, int));
+
+static struct demangle_component *
+d_operator_name PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_special_name PARAMS ((struct d_info *));
+
+static int
+d_call_offset PARAMS ((struct d_info *, int));
+
+static struct demangle_component *
+d_ctor_dtor_name PARAMS ((struct d_info *));
+
+static struct demangle_component **
+d_cv_qualifiers PARAMS ((struct d_info *, struct demangle_component **, int));
+
+static struct demangle_component *
+d_function_type PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_bare_function_type PARAMS ((struct d_info *, int));
+
+static struct demangle_component *
+d_class_enum_type PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_array_type PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_pointer_to_member_type PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_template_param PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_template_args PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_template_arg PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_expression PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_expr_primary PARAMS ((struct d_info *));
+
+static struct demangle_component *
+d_local_name PARAMS ((struct d_info *));
+
+static int
+d_discriminator PARAMS ((struct d_info *));
+
+static int
+d_add_substitution PARAMS ((struct d_info *, struct demangle_component *));
+
+static struct demangle_component *
+d_substitution PARAMS ((struct d_info *, int));
+
+static void
+d_print_resize PARAMS ((struct d_print_info *, size_t));
+
+static void
+d_print_append_char PARAMS ((struct d_print_info *, int));
+
+static void
+d_print_append_buffer PARAMS ((struct d_print_info *, const char *, size_t));
+
+static void
+d_print_error PARAMS ((struct d_print_info *));
+
+static void
+d_print_comp PARAMS ((struct d_print_info *,
+ const struct demangle_component *));
+
+static void
+d_print_java_identifier PARAMS ((struct d_print_info *, const char *, int));
+
+static void
+d_print_mod_list PARAMS ((struct d_print_info *, struct d_print_mod *, int));
+
+static void
+d_print_mod PARAMS ((struct d_print_info *,
+ const struct demangle_component *));
+
+static void
+d_print_function_type PARAMS ((struct d_print_info *,
+ const struct demangle_component *,
+ struct d_print_mod *));
+
+static void
+d_print_array_type PARAMS ((struct d_print_info *,
+ const struct demangle_component *,
+ struct d_print_mod *));
+
+static void
+d_print_expr_op PARAMS ((struct d_print_info *,
+ const struct demangle_component *));
+
+static void
+d_print_cast PARAMS ((struct d_print_info *,
+ const struct demangle_component *));
+
+static char *
+d_demangle PARAMS ((const char *, int, size_t *));
+
+#ifdef CP_DEMANGLE_DEBUG
+
+static void
+d_dump (dc, indent)
+ struct demangle_component *dc;
+ int indent;
+{
+ int i;
+
+ if (dc == NULL)
+ return;
+
+ for (i = 0; i < indent; ++i)
+ putchar (' ');
+
+ switch (dc->type)
+ {
+ case DEMANGLE_COMPONENT_NAME:
+ printf ("name '%.*s'\n", dc->u.s_name.len, dc->u.s_name.s);
+ return;
+ case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
+ printf ("template parameter %ld\n", dc->u.s_number.number);
+ return;
+ case DEMANGLE_COMPONENT_CTOR:
+ printf ("constructor %d\n", (int) dc->u.s_ctor.kind);
+ d_dump (dc->u.s_ctor.name, indent + 2);
+ return;
+ case DEMANGLE_COMPONENT_DTOR:
+ printf ("destructor %d\n", (int) dc->u.s_dtor.kind);
+ d_dump (dc->u.s_dtor.name, indent + 2);
+ return;
+ case DEMANGLE_COMPONENT_SUB_STD:
+ printf ("standard substitution %s\n", dc->u.s_string.string);
+ return;
+ case DEMANGLE_COMPONENT_BUILTIN_TYPE:
+ printf ("builtin type %s\n", dc->u.s_builtin.type->name);
+ return;
+ case DEMANGLE_COMPONENT_OPERATOR:
+ printf ("operator %s\n", dc->u.s_operator.op->name);
+ return;
+ case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+ printf ("extended operator with %d args\n",
+ dc->u.s_extended_operator.args);
+ d_dump (dc->u.s_extended_operator.name, indent + 2);
+ return;
+
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ printf ("qualified name\n");
+ break;
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ printf ("local name\n");
+ break;
+ case DEMANGLE_COMPONENT_TYPED_NAME:
+ printf ("typed name\n");
+ break;
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ printf ("template\n");
+ break;
+ case DEMANGLE_COMPONENT_VTABLE:
+ printf ("vtable\n");
+ break;
+ case DEMANGLE_COMPONENT_VTT:
+ printf ("VTT\n");
+ break;
+ case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE:
+ printf ("construction vtable\n");
+ break;
+ case DEMANGLE_COMPONENT_TYPEINFO:
+ printf ("typeinfo\n");
+ break;
+ case DEMANGLE_COMPONENT_TYPEINFO_NAME:
+ printf ("typeinfo name\n");
+ break;
+ case DEMANGLE_COMPONENT_TYPEINFO_FN:
+ printf ("typeinfo function\n");
+ break;
+ case DEMANGLE_COMPONENT_THUNK:
+ printf ("thunk\n");
+ break;
+ case DEMANGLE_COMPONENT_VIRTUAL_THUNK:
+ printf ("virtual thunk\n");
+ break;
+ case DEMANGLE_COMPONENT_COVARIANT_THUNK:
+ printf ("covariant thunk\n");
+ break;
+ case DEMANGLE_COMPONENT_JAVA_CLASS:
+ printf ("java class\n");
+ break;
+ case DEMANGLE_COMPONENT_GUARD:
+ printf ("guard\n");
+ break;
+ case DEMANGLE_COMPONENT_REFTEMP:
+ printf ("reference temporary\n");
+ break;
+ case DEMANGLE_COMPONENT_RESTRICT:
+ printf ("restrict\n");
+ break;
+ case DEMANGLE_COMPONENT_VOLATILE:
+ printf ("volatile\n");
+ break;
+ case DEMANGLE_COMPONENT_CONST:
+ printf ("const\n");
+ break;
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ printf ("restrict this\n");
+ break;
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ printf ("volatile this\n");
+ break;
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ printf ("const this\n");
+ break;
+ case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+ printf ("vendor type qualifier\n");
+ break;
+ case DEMANGLE_COMPONENT_POINTER:
+ printf ("pointer\n");
+ break;
+ case DEMANGLE_COMPONENT_REFERENCE:
+ printf ("reference\n");
+ break;
+ case DEMANGLE_COMPONENT_COMPLEX:
+ printf ("complex\n");
+ break;
+ case DEMANGLE_COMPONENT_IMAGINARY:
+ printf ("imaginary\n");
+ break;
+ case DEMANGLE_COMPONENT_VENDOR_TYPE:
+ printf ("vendor type\n");
+ break;
+ case DEMANGLE_COMPONENT_FUNCTION_TYPE:
+ printf ("function type\n");
+ break;
+ case DEMANGLE_COMPONENT_ARRAY_TYPE:
+ printf ("array type\n");
+ break;
+ case DEMANGLE_COMPONENT_PTRMEM_TYPE:
+ printf ("pointer to member type\n");
+ break;
+ case DEMANGLE_COMPONENT_ARGLIST:
+ printf ("argument list\n");
+ break;
+ case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+ printf ("template argument list\n");
+ break;
+ case DEMANGLE_COMPONENT_CAST:
+ printf ("cast\n");
+ break;
+ case DEMANGLE_COMPONENT_UNARY:
+ printf ("unary operator\n");
+ break;
+ case DEMANGLE_COMPONENT_BINARY:
+ printf ("binary operator\n");
+ break;
+ case DEMANGLE_COMPONENT_BINARY_ARGS:
+ printf ("binary operator arguments\n");
+ break;
+ case DEMANGLE_COMPONENT_TRINARY:
+ printf ("trinary operator\n");
+ break;
+ case DEMANGLE_COMPONENT_TRINARY_ARG1:
+ printf ("trinary operator arguments 1\n");
+ break;
+ case DEMANGLE_COMPONENT_TRINARY_ARG2:
+ printf ("trinary operator arguments 1\n");
+ break;
+ case DEMANGLE_COMPONENT_LITERAL:
+ printf ("literal\n");
+ break;
+ case DEMANGLE_COMPONENT_LITERAL_NEG:
+ printf ("negative literal\n");
+ break;
+ }
+
+ d_dump (d_left (dc), indent + 2);
+ d_dump (d_right (dc), indent + 2);
+}
+
+#endif /* CP_DEMANGLE_DEBUG */
+
+/* Fill in a DEMANGLE_COMPONENT_NAME. */
+
+CP_STATIC_IF_GLIBCPP_V3
+int
+cplus_demangle_fill_name (p, s, len)
+ struct demangle_component *p;
+ const char *s;
+ int len;
+{
+ if (p == NULL || s == NULL || len == 0)
+ return 0;
+ p->type = DEMANGLE_COMPONENT_NAME;
+ p->u.s_name.s = s;
+ p->u.s_name.len = len;
+ return 1;
+}
+
+/* Fill in a DEMANGLE_COMPONENT_EXTENDED_OPERATOR. */
+
+CP_STATIC_IF_GLIBCPP_V3
+int
+cplus_demangle_fill_extended_operator (p, args, name)
+ struct demangle_component *p;
+ int args;
+ struct demangle_component *name;
+{
+ if (p == NULL || args < 0 || name == NULL)
+ return 0;
+ p->type = DEMANGLE_COMPONENT_EXTENDED_OPERATOR;
+ p->u.s_extended_operator.args = args;
+ p->u.s_extended_operator.name = name;
+ return 1;
+}
+
+/* Fill in a DEMANGLE_COMPONENT_CTOR. */
+
+CP_STATIC_IF_GLIBCPP_V3
+int
+cplus_demangle_fill_ctor (p, kind, name)
+ struct demangle_component *p;
+ enum gnu_v3_ctor_kinds kind;
+ struct demangle_component *name;
+{
+ if (p == NULL
+ || name == NULL
+ || (kind < gnu_v3_complete_object_ctor
+ && kind > gnu_v3_complete_object_allocating_ctor))
+ return 0;
+ p->type = DEMANGLE_COMPONENT_CTOR;
+ p->u.s_ctor.kind = kind;
+ p->u.s_ctor.name = name;
+ return 1;
+}
+
+/* Fill in a DEMANGLE_COMPONENT_DTOR. */
+
+CP_STATIC_IF_GLIBCPP_V3
+int
+cplus_demangle_fill_dtor (p, kind, name)
+ struct demangle_component *p;
+ enum gnu_v3_dtor_kinds kind;
+ struct demangle_component *name;
+{
+ if (p == NULL
+ || name == NULL
+ || (kind < gnu_v3_deleting_dtor
+ && kind > gnu_v3_base_object_dtor))
+ return 0;
+ p->type = DEMANGLE_COMPONENT_DTOR;
+ p->u.s_dtor.kind = kind;
+ p->u.s_dtor.name = name;
+ return 1;
+}
+
+/* Add a new component. */
+
+static struct demangle_component *
+d_make_empty (di)
+ struct d_info *di;
+{
+ struct demangle_component *p;
+
+ if (di->next_comp >= di->num_comps)
+ return NULL;
+ p = &di->comps[di->next_comp];
+ ++di->next_comp;
+ return p;
+}
+
+/* Add a new generic component. */
+
+static struct demangle_component *
+d_make_comp (di, type, left, right)
+ struct d_info *di;
+ enum demangle_component_type type;
+ struct demangle_component *left;
+ struct demangle_component *right;
+{
+ struct demangle_component *p;
+
+ /* We check for errors here. A typical error would be a NULL return
+ from a subroutine. We catch those here, and return NULL
+ upward. */
+ switch (type)
+ {
+ /* These types require two parameters. */
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ case DEMANGLE_COMPONENT_TYPED_NAME:
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE:
+ case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+ case DEMANGLE_COMPONENT_PTRMEM_TYPE:
+ case DEMANGLE_COMPONENT_UNARY:
+ case DEMANGLE_COMPONENT_BINARY:
+ case DEMANGLE_COMPONENT_BINARY_ARGS:
+ case DEMANGLE_COMPONENT_TRINARY:
+ case DEMANGLE_COMPONENT_TRINARY_ARG1:
+ case DEMANGLE_COMPONENT_TRINARY_ARG2:
+ case DEMANGLE_COMPONENT_LITERAL:
+ case DEMANGLE_COMPONENT_LITERAL_NEG:
+ if (left == NULL || right == NULL)
+ return NULL;
+ break;
+
+ /* These types only require one parameter. */
+ case DEMANGLE_COMPONENT_VTABLE:
+ case DEMANGLE_COMPONENT_VTT:
+ case DEMANGLE_COMPONENT_TYPEINFO:
+ case DEMANGLE_COMPONENT_TYPEINFO_NAME:
+ case DEMANGLE_COMPONENT_TYPEINFO_FN:
+ case DEMANGLE_COMPONENT_THUNK:
+ case DEMANGLE_COMPONENT_VIRTUAL_THUNK:
+ case DEMANGLE_COMPONENT_COVARIANT_THUNK:
+ case DEMANGLE_COMPONENT_JAVA_CLASS:
+ case DEMANGLE_COMPONENT_GUARD:
+ case DEMANGLE_COMPONENT_REFTEMP:
+ case DEMANGLE_COMPONENT_POINTER:
+ case DEMANGLE_COMPONENT_REFERENCE:
+ case DEMANGLE_COMPONENT_COMPLEX:
+ case DEMANGLE_COMPONENT_IMAGINARY:
+ case DEMANGLE_COMPONENT_VENDOR_TYPE:
+ case DEMANGLE_COMPONENT_ARGLIST:
+ case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+ case DEMANGLE_COMPONENT_CAST:
+ if (left == NULL)
+ return NULL;
+ break;
+
+ /* This needs a right parameter, but the left parameter can be
+ empty. */
+ case DEMANGLE_COMPONENT_ARRAY_TYPE:
+ if (right == NULL)
+ return NULL;
+ break;
+
+ /* These are allowed to have no parameters--in some cases they
+ will be filled in later. */
+ case DEMANGLE_COMPONENT_FUNCTION_TYPE:
+ case DEMANGLE_COMPONENT_RESTRICT:
+ case DEMANGLE_COMPONENT_VOLATILE:
+ case DEMANGLE_COMPONENT_CONST:
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ break;
+
+ /* Other types should not be seen here. */
+ default:
+ return NULL;
+ }
+
+ p = d_make_empty (di);
+ if (p != NULL)
+ {
+ p->type = type;
+ p->u.s_binary.left = left;
+ p->u.s_binary.right = right;
+ }
+ return p;
+}
+
+/* Add a new name component. */
+
+static struct demangle_component *
+d_make_name (di, s, len)
+ struct d_info *di;
+ const char *s;
+ int len;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (! cplus_demangle_fill_name (p, s, len))
+ return NULL;
+ return p;
+}
+
+/* Add a new builtin type component. */
+
+static struct demangle_component *
+d_make_builtin_type (di, type)
+ struct d_info *di;
+ const struct demangle_builtin_type_info *type;
+{
+ struct demangle_component *p;
+
+ if (type == NULL)
+ return NULL;
+ p = d_make_empty (di);
+ if (p != NULL)
+ {
+ p->type = DEMANGLE_COMPONENT_BUILTIN_TYPE;
+ p->u.s_builtin.type = type;
+ }
+ return p;
+}
+
+/* Add a new operator component. */
+
+static struct demangle_component *
+d_make_operator (di, op)
+ struct d_info *di;
+ const struct demangle_operator_info *op;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (p != NULL)
+ {
+ p->type = DEMANGLE_COMPONENT_OPERATOR;
+ p->u.s_operator.op = op;
+ }
+ return p;
+}
+
+/* Add a new extended operator component. */
+
+static struct demangle_component *
+d_make_extended_operator (di, args, name)
+ struct d_info *di;
+ int args;
+ struct demangle_component *name;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (! cplus_demangle_fill_extended_operator (p, args, name))
+ return NULL;
+ return p;
+}
+
+/* Add a new constructor component. */
+
+static struct demangle_component *
+d_make_ctor (di, kind, name)
+ struct d_info *di;
+ enum gnu_v3_ctor_kinds kind;
+ struct demangle_component *name;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (! cplus_demangle_fill_ctor (p, kind, name))
+ return NULL;
+ return p;
+}
+
+/* Add a new destructor component. */
+
+static struct demangle_component *
+d_make_dtor (di, kind, name)
+ struct d_info *di;
+ enum gnu_v3_dtor_kinds kind;
+ struct demangle_component *name;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (! cplus_demangle_fill_dtor (p, kind, name))
+ return NULL;
+ return p;
+}
+
+/* Add a new template parameter. */
+
+static struct demangle_component *
+d_make_template_param (di, i)
+ struct d_info *di;
+ long i;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (p != NULL)
+ {
+ p->type = DEMANGLE_COMPONENT_TEMPLATE_PARAM;
+ p->u.s_number.number = i;
+ }
+ return p;
+}
+
+/* Add a new standard substitution component. */
+
+static struct demangle_component *
+d_make_sub (di, name, len)
+ struct d_info *di;
+ const char *name;
+ int len;
+{
+ struct demangle_component *p;
+
+ p = d_make_empty (di);
+ if (p != NULL)
+ {
+ p->type = DEMANGLE_COMPONENT_SUB_STD;
+ p->u.s_string.string = name;
+ p->u.s_string.len = len;
+ }
+ return p;
+}
+
+/* <mangled-name> ::= _Z <encoding>
+
+ TOP_LEVEL is non-zero when called at the top level. */
+
+CP_STATIC_IF_GLIBCPP_V3
+struct demangle_component *
+cplus_demangle_mangled_name (di, top_level)
+ struct d_info *di;
+ int top_level;
+{
+ if (d_next_char (di) != '_')
+ return NULL;
+ if (d_next_char (di) != 'Z')
+ return NULL;
+ return d_encoding (di, top_level);
+}
+
+/* Return whether a function should have a return type. The argument
+ is the function name, which may be qualified in various ways. The
+ rules are that template functions have return types with some
+ exceptions, function types which are not part of a function name
+ mangling have return types with some exceptions, and non-template
+ function names do not have return types. The exceptions are that
+ constructors, destructors, and conversion operators do not have
+ return types. */
+
+static int
+has_return_type (dc)
+ struct demangle_component *dc;
+{
+ if (dc == NULL)
+ return 0;
+ switch (dc->type)
+ {
+ default:
+ return 0;
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ return ! is_ctor_dtor_or_conversion (d_left (dc));
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ return has_return_type (d_left (dc));
+ }
+}
+
+/* Return whether a name is a constructor, a destructor, or a
+ conversion operator. */
+
+static int
+is_ctor_dtor_or_conversion (dc)
+ struct demangle_component *dc;
+{
+ if (dc == NULL)
+ return 0;
+ switch (dc->type)
+ {
+ default:
+ return 0;
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ return is_ctor_dtor_or_conversion (d_right (dc));
+ case DEMANGLE_COMPONENT_CTOR:
+ case DEMANGLE_COMPONENT_DTOR:
+ case DEMANGLE_COMPONENT_CAST:
+ return 1;
+ }
+}
+
+/* <encoding> ::= <(function) name> <bare-function-type>
+ ::= <(data) name>
+ ::= <special-name>
+
+ TOP_LEVEL is non-zero when called at the top level, in which case
+ if DMGL_PARAMS is not set we do not demangle the function
+ parameters. We only set this at the top level, because otherwise
+ we would not correctly demangle names in local scopes. */
+
+static struct demangle_component *
+d_encoding (di, top_level)
+ struct d_info *di;
+ int top_level;
+{
+ char peek = d_peek_char (di);
+
+ if (peek == 'G' || peek == 'T')
+ return d_special_name (di);
+ else
+ {
+ struct demangle_component *dc;
+
+ dc = d_name (di);
+
+ if (dc != NULL && top_level && (di->options & DMGL_PARAMS) == 0)
+ {
+ /* Strip off any initial CV-qualifiers, as they really apply
+ to the `this' parameter, and they were not output by the
+ v2 demangler without DMGL_PARAMS. */
+ while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS
+ || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
+ || dc->type == DEMANGLE_COMPONENT_CONST_THIS)
+ dc = d_left (dc);
+
+ /* If the top level is a DEMANGLE_COMPONENT_LOCAL_NAME, then
+ there may be CV-qualifiers on its right argument which
+ really apply here; this happens when parsing a class
+ which is local to a function. */
+ if (dc->type == DEMANGLE_COMPONENT_LOCAL_NAME)
+ {
+ struct demangle_component *dcr;
+
+ dcr = d_right (dc);
+ while (dcr->type == DEMANGLE_COMPONENT_RESTRICT_THIS
+ || dcr->type == DEMANGLE_COMPONENT_VOLATILE_THIS
+ || dcr->type == DEMANGLE_COMPONENT_CONST_THIS)
+ dcr = d_left (dcr);
+ dc->u.s_binary.right = dcr;
+ }
+
+ return dc;
+ }
+
+ peek = d_peek_char (di);
+ if (peek == '\0' || peek == 'E')
+ return dc;
+ return d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME, dc,
+ d_bare_function_type (di, has_return_type (dc)));
+ }
+}
+
+/* <name> ::= <nested-name>
+ ::= <unscoped-name>
+ ::= <unscoped-template-name> <template-args>
+ ::= <local-name>
+
+ <unscoped-name> ::= <unqualified-name>
+ ::= St <unqualified-name>
+
+ <unscoped-template-name> ::= <unscoped-name>
+ ::= <substitution>
+*/
+
+static struct demangle_component *
+d_name (di)
+ struct d_info *di;
+{
+ char peek = d_peek_char (di);
+ struct demangle_component *dc;
+
+ switch (peek)
+ {
+ case 'N':
+ return d_nested_name (di);
+
+ case 'Z':
+ return d_local_name (di);
+
+ case 'S':
+ {
+ int subst;
+
+ if (d_peek_next_char (di) != 't')
+ {
+ dc = d_substitution (di, 0);
+ subst = 1;
+ }
+ else
+ {
+ d_advance (di, 2);
+ dc = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME,
+ d_make_name (di, "std", 3),
+ d_unqualified_name (di));
+ di->expansion += 3;
+ subst = 0;
+ }
+
+ if (d_peek_char (di) != 'I')
+ {
+ /* The grammar does not permit this case to occur if we
+ called d_substitution() above (i.e., subst == 1). We
+ don't bother to check. */
+ }
+ else
+ {
+ /* This is <template-args>, which means that we just saw
+ <unscoped-template-name>, which is a substitution
+ candidate if we didn't just get it from a
+ substitution. */
+ if (! subst)
+ {
+ if (! d_add_substitution (di, dc))
+ return NULL;
+ }
+ dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc,
+ d_template_args (di));
+ }
+
+ return dc;
+ }
+
+ default:
+ dc = d_unqualified_name (di);
+ if (d_peek_char (di) == 'I')
+ {
+ /* This is <template-args>, which means that we just saw
+ <unscoped-template-name>, which is a substitution
+ candidate. */
+ if (! d_add_substitution (di, dc))
+ return NULL;
+ dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc,
+ d_template_args (di));
+ }
+ return dc;
+ }
+}
+
+/* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
+ ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
+*/
+
+static struct demangle_component *
+d_nested_name (di)
+ struct d_info *di;
+{
+ struct demangle_component *ret;
+ struct demangle_component **pret;
+
+ if (d_next_char (di) != 'N')
+ return NULL;
+
+ pret = d_cv_qualifiers (di, &ret, 1);
+ if (pret == NULL)
+ return NULL;
+
+ *pret = d_prefix (di);
+ if (*pret == NULL)
+ return NULL;
+
+ if (d_next_char (di) != 'E')
+ return NULL;
+
+ return ret;
+}
+
+/* <prefix> ::= <prefix> <unqualified-name>
+ ::= <template-prefix> <template-args>
+ ::= <template-param>
+ ::=
+ ::= <substitution>
+
+ <template-prefix> ::= <prefix> <(template) unqualified-name>
+ ::= <template-param>
+ ::= <substitution>
+*/
+
+static struct demangle_component *
+d_prefix (di)
+ struct d_info *di;
+{
+ struct demangle_component *ret = NULL;
+
+ while (1)
+ {
+ char peek;
+ enum demangle_component_type comb_type;
+ struct demangle_component *dc;
+
+ peek = d_peek_char (di);
+ if (peek == '\0')
+ return NULL;
+
+ /* The older code accepts a <local-name> here, but I don't see
+ that in the grammar. The older code does not accept a
+ <template-param> here. */
+
+ comb_type = DEMANGLE_COMPONENT_QUAL_NAME;
+ if (IS_DIGIT (peek)
+ || IS_LOWER (peek)
+ || peek == 'C'
+ || peek == 'D'
+ || peek == 'L')
+ dc = d_unqualified_name (di);
+ else if (peek == 'S')
+ dc = d_substitution (di, 1);
+ else if (peek == 'I')
+ {
+ if (ret == NULL)
+ return NULL;
+ comb_type = DEMANGLE_COMPONENT_TEMPLATE;
+ dc = d_template_args (di);
+ }
+ else if (peek == 'T')
+ dc = d_template_param (di);
+ else if (peek == 'E')
+ return ret;
+ else
+ return NULL;
+
+ if (ret == NULL)
+ ret = dc;
+ else
+ ret = d_make_comp (di, comb_type, ret, dc);
+
+ if (peek != 'S' && d_peek_char (di) != 'E')
+ {
+ if (! d_add_substitution (di, ret))
+ return NULL;
+ }
+ }
+}
+
+/* <unqualified-name> ::= <operator-name>
+ ::= <ctor-dtor-name>
+ ::= <source-name>
+ ::= <local-source-name>
+
+ <local-source-anem> ::= L <source-name><discriminator>
+*/
+
+static struct demangle_component *
+d_unqualified_name (di)
+ struct d_info *di;
+{
+ char peek;
+
+ peek = d_peek_char (di);
+ if (IS_DIGIT (peek))
+ return d_source_name (di);
+ else if (IS_LOWER (peek))
+ {
+ struct demangle_component *ret;
+
+ ret = d_operator_name (di);
+ if (ret != NULL && ret->type == DEMANGLE_COMPONENT_OPERATOR)
+ di->expansion += sizeof "operator" + ret->u.s_operator.op->len - 2;
+ return ret;
+ }
+ else if (peek == 'C' || peek == 'D')
+ return d_ctor_dtor_name (di);
+ else if (peek == 'L')
+ {
+ struct demangle_component * ret;
+
+ d_advance (di, 1);
+
+ ret = d_source_name (di);
+ if (ret == NULL)
+ return NULL;
+ if (! d_discriminator (di))
+ return NULL;
+ return ret;
+ }
+ else
+ return NULL;
+}
+
+/* <source-name> ::= <(positive length) number> <identifier> */
+
+static struct demangle_component *
+d_source_name (di)
+ struct d_info *di;
+{
+ long len;
+ struct demangle_component *ret;
+
+ len = d_number (di);
+ if (len <= 0)
+ return NULL;
+ ret = d_identifier (di, len);
+ di->last_name = ret;
+ return ret;
+}
+
+/* number ::= [n] <(non-negative decimal integer)> */
+
+static long
+d_number (di)
+ struct d_info *di;
+{
+ int negative;
+ char peek;
+ long ret;
+
+ negative = 0;
+ peek = d_peek_char (di);
+ if (peek == 'n')
+ {
+ negative = 1;
+ d_advance (di, 1);
+ peek = d_peek_char (di);
+ }
+
+ ret = 0;
+ while (1)
+ {
+ if (! IS_DIGIT (peek))
+ {
+ if (negative)
+ ret = - ret;
+ return ret;
+ }
+ ret = ret * 10 + peek - '0';
+ d_advance (di, 1);
+ peek = d_peek_char (di);
+ }
+}
+
+/* identifier ::= <(unqualified source code identifier)> */
+
+static struct demangle_component *
+d_identifier (di, len)
+ struct d_info *di;
+ int len;
+{
+ const char *name;
+
+ name = d_str (di);
+
+ if (di->send - name < len)
+ return NULL;
+
+ d_advance (di, len);
+
+ /* A Java mangled name may have a trailing '$' if it is a C++
+ keyword. This '$' is not included in the length count. We just
+ ignore the '$'. */
+ if ((di->options & DMGL_JAVA) != 0
+ && d_peek_char (di) == '$')
+ d_advance (di, 1);
+
+ /* Look for something which looks like a gcc encoding of an
+ anonymous namespace, and replace it with a more user friendly
+ name. */
+ if (len >= (int) ANONYMOUS_NAMESPACE_PREFIX_LEN + 2
+ && memcmp (name, ANONYMOUS_NAMESPACE_PREFIX,
+ ANONYMOUS_NAMESPACE_PREFIX_LEN) == 0)
+ {
+ const char *s;
+
+ s = name + ANONYMOUS_NAMESPACE_PREFIX_LEN;
+ if ((*s == '.' || *s == '_' || *s == '$')
+ && s[1] == 'N')
+ {
+ di->expansion -= len - sizeof "(anonymous namespace)";
+ return d_make_name (di, "(anonymous namespace)",
+ sizeof "(anonymous namespace)" - 1);
+ }
+ }
+
+ return d_make_name (di, name, len);
+}
+
+/* operator_name ::= many different two character encodings.
+ ::= cv <type>
+ ::= v <digit> <source-name>
+*/
+
+#define NL(s) s, (sizeof s) - 1
+
+CP_STATIC_IF_GLIBCPP_V3
+const struct demangle_operator_info cplus_demangle_operators[] =
+{
+ { "aN", NL ("&="), 2 },
+ { "aS", NL ("="), 2 },
+ { "aa", NL ("&&"), 2 },
+ { "ad", NL ("&"), 1 },
+ { "an", NL ("&"), 2 },
+ { "cl", NL ("()"), 0 },
+ { "cm", NL (","), 2 },
+ { "co", NL ("~"), 1 },
+ { "dV", NL ("/="), 2 },
+ { "da", NL ("delete[]"), 1 },
+ { "de", NL ("*"), 1 },
+ { "dl", NL ("delete"), 1 },
+ { "dv", NL ("/"), 2 },
+ { "eO", NL ("^="), 2 },
+ { "eo", NL ("^"), 2 },
+ { "eq", NL ("=="), 2 },
+ { "ge", NL (">="), 2 },
+ { "gt", NL (">"), 2 },
+ { "ix", NL ("[]"), 2 },
+ { "lS", NL ("<<="), 2 },
+ { "le", NL ("<="), 2 },
+ { "ls", NL ("<<"), 2 },
+ { "lt", NL ("<"), 2 },
+ { "mI", NL ("-="), 2 },
+ { "mL", NL ("*="), 2 },
+ { "mi", NL ("-"), 2 },
+ { "ml", NL ("*"), 2 },
+ { "mm", NL ("--"), 1 },
+ { "na", NL ("new[]"), 1 },
+ { "ne", NL ("!="), 2 },
+ { "ng", NL ("-"), 1 },
+ { "nt", NL ("!"), 1 },
+ { "nw", NL ("new"), 1 },
+ { "oR", NL ("|="), 2 },
+ { "oo", NL ("||"), 2 },
+ { "or", NL ("|"), 2 },
+ { "pL", NL ("+="), 2 },
+ { "pl", NL ("+"), 2 },
+ { "pm", NL ("->*"), 2 },
+ { "pp", NL ("++"), 1 },
+ { "ps", NL ("+"), 1 },
+ { "pt", NL ("->"), 2 },
+ { "qu", NL ("?"), 3 },
+ { "rM", NL ("%="), 2 },
+ { "rS", NL (">>="), 2 },
+ { "rm", NL ("%"), 2 },
+ { "rs", NL (">>"), 2 },
+ { "st", NL ("sizeof "), 1 },
+ { "sz", NL ("sizeof "), 1 },
+ { NULL, NULL, 0, 0 }
+};
+
+static struct demangle_component *
+d_operator_name (di)
+ struct d_info *di;
+{
+ char c1;
+ char c2;
+
+ c1 = d_next_char (di);
+ c2 = d_next_char (di);
+ if (c1 == 'v' && IS_DIGIT (c2))
+ return d_make_extended_operator (di, c2 - '0', d_source_name (di));
+ else if (c1 == 'c' && c2 == 'v')
+ return d_make_comp (di, DEMANGLE_COMPONENT_CAST,
+ cplus_demangle_type (di), NULL);
+ else
+ {
+ /* LOW is the inclusive lower bound. */
+ int low = 0;
+ /* HIGH is the exclusive upper bound. We subtract one to ignore
+ the sentinel at the end of the array. */
+ int high = ((sizeof (cplus_demangle_operators)
+ / sizeof (cplus_demangle_operators[0]))
+ - 1);
+
+ while (1)
+ {
+ int i;
+ const struct demangle_operator_info *p;
+
+ i = low + (high - low) / 2;
+ p = cplus_demangle_operators + i;
+
+ if (c1 == p->code[0] && c2 == p->code[1])
+ return d_make_operator (di, p);
+
+ if (c1 < p->code[0] || (c1 == p->code[0] && c2 < p->code[1]))
+ high = i;
+ else
+ low = i + 1;
+ if (low == high)
+ return NULL;
+ }
+ }
+}
+
+/* <special-name> ::= TV <type>
+ ::= TT <type>
+ ::= TI <type>
+ ::= TS <type>
+ ::= GV <(object) name>
+ ::= T <call-offset> <(base) encoding>
+ ::= Tc <call-offset> <call-offset> <(base) encoding>
+ Also g++ extensions:
+ ::= TC <type> <(offset) number> _ <(base) type>
+ ::= TF <type>
+ ::= TJ <type>
+ ::= GR <name>
+*/
+
+static struct demangle_component *
+d_special_name (di)
+ struct d_info *di;
+{
+ char c;
+
+ di->expansion += 20;
+ c = d_next_char (di);
+ if (c == 'T')
+ {
+ switch (d_next_char (di))
+ {
+ case 'V':
+ di->expansion -= 5;
+ return d_make_comp (di, DEMANGLE_COMPONENT_VTABLE,
+ cplus_demangle_type (di), NULL);
+ case 'T':
+ di->expansion -= 10;
+ return d_make_comp (di, DEMANGLE_COMPONENT_VTT,
+ cplus_demangle_type (di), NULL);
+ case 'I':
+ return d_make_comp (di, DEMANGLE_COMPONENT_TYPEINFO,
+ cplus_demangle_type (di), NULL);
+ case 'S':
+ return d_make_comp (di, DEMANGLE_COMPONENT_TYPEINFO_NAME,
+ cplus_demangle_type (di), NULL);
+
+ case 'h':
+ if (! d_call_offset (di, 'h'))
+ return NULL;
+ return d_make_comp (di, DEMANGLE_COMPONENT_THUNK,
+ d_encoding (di, 0), NULL);
+
+ case 'v':
+ if (! d_call_offset (di, 'v'))
+ return NULL;
+ return d_make_comp (di, DEMANGLE_COMPONENT_VIRTUAL_THUNK,
+ d_encoding (di, 0), NULL);
+
+ case 'c':
+ if (! d_call_offset (di, '\0'))
+ return NULL;
+ if (! d_call_offset (di, '\0'))
+ return NULL;
+ return d_make_comp (di, DEMANGLE_COMPONENT_COVARIANT_THUNK,
+ d_encoding (di, 0), NULL);
+
+ case 'C':
+ {
+ struct demangle_component *derived_type;
+ long offset;
+ struct demangle_component *base_type;
+
+ derived_type = cplus_demangle_type (di);
+ offset = d_number (di);
+ if (offset < 0)
+ return NULL;
+ if (d_next_char (di) != '_')
+ return NULL;
+ base_type = cplus_demangle_type (di);
+ /* We don't display the offset. FIXME: We should display
+ it in verbose mode. */
+ di->expansion += 5;
+ return d_make_comp (di, DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE,
+ base_type, derived_type);
+ }
+
+ case 'F':
+ return d_make_comp (di, DEMANGLE_COMPONENT_TYPEINFO_FN,
+ cplus_demangle_type (di), NULL);
+ case 'J':
+ return d_make_comp (di, DEMANGLE_COMPONENT_JAVA_CLASS,
+ cplus_demangle_type (di), NULL);
+
+ default:
+ return NULL;
+ }
+ }
+ else if (c == 'G')
+ {
+ switch (d_next_char (di))
+ {
+ case 'V':
+ return d_make_comp (di, DEMANGLE_COMPONENT_GUARD, d_name (di), NULL);
+
+ case 'R':
+ return d_make_comp (di, DEMANGLE_COMPONENT_REFTEMP, d_name (di),
+ NULL);
+
+ default:
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+}
+
+/* <call-offset> ::= h <nv-offset> _
+ ::= v <v-offset> _
+
+ <nv-offset> ::= <(offset) number>
+
+ <v-offset> ::= <(offset) number> _ <(virtual offset) number>
+
+ The C parameter, if not '\0', is a character we just read which is
+ the start of the <call-offset>.
+
+ We don't display the offset information anywhere. FIXME: We should
+ display it in verbose mode. */
+
+static int
+d_call_offset (di, c)
+ struct d_info *di;
+ int c;
+{
+ if (c == '\0')
+ c = d_next_char (di);
+
+ if (c == 'h')
+ d_number (di);
+ else if (c == 'v')
+ {
+ d_number (di);
+ if (d_next_char (di) != '_')
+ return 0;
+ d_number (di);
+ }
+ else
+ return 0;
+
+ if (d_next_char (di) != '_')
+ return 0;
+
+ return 1;
+}
+
+/* <ctor-dtor-name> ::= C1
+ ::= C2
+ ::= C3
+ ::= D0
+ ::= D1
+ ::= D2
+*/
+
+static struct demangle_component *
+d_ctor_dtor_name (di)
+ struct d_info *di;
+{
+ if (di->last_name != NULL)
+ {
+ if (di->last_name->type == DEMANGLE_COMPONENT_NAME)
+ di->expansion += di->last_name->u.s_name.len;
+ else if (di->last_name->type == DEMANGLE_COMPONENT_SUB_STD)
+ di->expansion += di->last_name->u.s_string.len;
+ }
+ switch (d_next_char (di))
+ {
+ case 'C':
+ {
+ enum gnu_v3_ctor_kinds kind;
+
+ switch (d_next_char (di))
+ {
+ case '1':
+ kind = gnu_v3_complete_object_ctor;
+ break;
+ case '2':
+ kind = gnu_v3_base_object_ctor;
+ break;
+ case '3':
+ kind = gnu_v3_complete_object_allocating_ctor;
+ break;
+ default:
+ return NULL;
+ }
+ return d_make_ctor (di, kind, di->last_name);
+ }
+
+ case 'D':
+ {
+ enum gnu_v3_dtor_kinds kind;
+
+ switch (d_next_char (di))
+ {
+ case '0':
+ kind = gnu_v3_deleting_dtor;
+ break;
+ case '1':
+ kind = gnu_v3_complete_object_dtor;
+ break;
+ case '2':
+ kind = gnu_v3_base_object_dtor;
+ break;
+ default:
+ return NULL;
+ }
+ return d_make_dtor (di, kind, di->last_name);
+ }
+
+ default:
+ return NULL;
+ }
+}
+
+/* <type> ::= <builtin-type>
+ ::= <function-type>
+ ::= <class-enum-type>
+ ::= <array-type>
+ ::= <pointer-to-member-type>
+ ::= <template-param>
+ ::= <template-template-param> <template-args>
+ ::= <substitution>
+ ::= <CV-qualifiers> <type>
+ ::= P <type>
+ ::= R <type>
+ ::= C <type>
+ ::= G <type>
+ ::= U <source-name> <type>
+
+ <builtin-type> ::= various one letter codes
+ ::= u <source-name>
+*/
+
+CP_STATIC_IF_GLIBCPP_V3
+const struct demangle_builtin_type_info
+cplus_demangle_builtin_types[D_BUILTIN_TYPE_COUNT] =
+{
+ /* a */ { NL ("signed char"), NL ("signed char"), D_PRINT_DEFAULT },
+ /* b */ { NL ("bool"), NL ("boolean"), D_PRINT_BOOL },
+ /* c */ { NL ("char"), NL ("byte"), D_PRINT_DEFAULT },
+ /* d */ { NL ("double"), NL ("double"), D_PRINT_FLOAT },
+ /* e */ { NL ("long double"), NL ("long double"), D_PRINT_FLOAT },
+ /* f */ { NL ("float"), NL ("float"), D_PRINT_FLOAT },
+ /* g */ { NL ("__float128"), NL ("__float128"), D_PRINT_FLOAT },
+ /* h */ { NL ("unsigned char"), NL ("unsigned char"), D_PRINT_DEFAULT },
+ /* i */ { NL ("int"), NL ("int"), D_PRINT_INT },
+ /* j */ { NL ("unsigned int"), NL ("unsigned"), D_PRINT_UNSIGNED },
+ /* k */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT },
+ /* l */ { NL ("long"), NL ("long"), D_PRINT_LONG },
+ /* m */ { NL ("unsigned long"), NL ("unsigned long"), D_PRINT_UNSIGNED_LONG },
+ /* n */ { NL ("__int128"), NL ("__int128"), D_PRINT_DEFAULT },
+ /* o */ { NL ("unsigned __int128"), NL ("unsigned __int128"),
+ D_PRINT_DEFAULT },
+ /* p */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT },
+ /* q */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT },
+ /* r */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT },
+ /* s */ { NL ("short"), NL ("short"), D_PRINT_DEFAULT },
+ /* t */ { NL ("unsigned short"), NL ("unsigned short"), D_PRINT_DEFAULT },
+ /* u */ { NULL, 0, NULL, 0, D_PRINT_DEFAULT },
+ /* v */ { NL ("void"), NL ("void"), D_PRINT_VOID },
+ /* w */ { NL ("wchar_t"), NL ("char"), D_PRINT_DEFAULT },
+ /* x */ { NL ("long long"), NL ("long"), D_PRINT_LONG_LONG },
+ /* y */ { NL ("unsigned long long"), NL ("unsigned long long"),
+ D_PRINT_UNSIGNED_LONG_LONG },
+ /* z */ { NL ("..."), NL ("..."), D_PRINT_DEFAULT },
+};
+
+CP_STATIC_IF_GLIBCPP_V3
+struct demangle_component *
+cplus_demangle_type (di)
+ struct d_info *di;
+{
+ char peek;
+ struct demangle_component *ret;
+ int can_subst;
+
+ /* The ABI specifies that when CV-qualifiers are used, the base type
+ is substitutable, and the fully qualified type is substitutable,
+ but the base type with a strict subset of the CV-qualifiers is
+ not substitutable. The natural recursive implementation of the
+ CV-qualifiers would cause subsets to be substitutable, so instead
+ we pull them all off now.
+
+ FIXME: The ABI says that order-insensitive vendor qualifiers
+ should be handled in the same way, but we have no way to tell
+ which vendor qualifiers are order-insensitive and which are
+ order-sensitive. So we just assume that they are all
+ order-sensitive. g++ 3.4 supports only one vendor qualifier,
+ __vector, and it treats it as order-sensitive when mangling
+ names. */
+
+ peek = d_peek_char (di);
+ if (peek == 'r' || peek == 'V' || peek == 'K')
+ {
+ struct demangle_component **pret;
+
+ pret = d_cv_qualifiers (di, &ret, 0);
+ if (pret == NULL)
+ return NULL;
+ *pret = cplus_demangle_type (di);
+ if (! d_add_substitution (di, ret))
+ return NULL;
+ return ret;
+ }
+
+ can_subst = 1;
+
+ switch (peek)
+ {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'l': case 'm': case 'n':
+ case 'o': case 's': case 't':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ ret = d_make_builtin_type (di,
+ &cplus_demangle_builtin_types[peek - 'a']);
+ di->expansion += ret->u.s_builtin.type->len;
+ can_subst = 0;
+ d_advance (di, 1);
+ break;
+
+ case 'u':
+ d_advance (di, 1);
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_TYPE,
+ d_source_name (di), NULL);
+ break;
+
+ case 'F':
+ ret = d_function_type (di);
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'N':
+ case 'Z':
+ ret = d_class_enum_type (di);
+ break;
+
+ case 'A':
+ ret = d_array_type (di);
+ break;
+
+ case 'M':
+ ret = d_pointer_to_member_type (di);
+ break;
+
+ case 'T':
+ ret = d_template_param (di);
+ if (d_peek_char (di) == 'I')
+ {
+ /* This is <template-template-param> <template-args>. The
+ <template-template-param> part is a substitution
+ candidate. */
+ if (! d_add_substitution (di, ret))
+ return NULL;
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret,
+ d_template_args (di));
+ }
+ break;
+
+ case 'S':
+ /* If this is a special substitution, then it is the start of
+ <class-enum-type>. */
+ {
+ char peek_next;
+
+ peek_next = d_peek_next_char (di);
+ if (IS_DIGIT (peek_next)
+ || peek_next == '_'
+ || IS_UPPER (peek_next))
+ {
+ ret = d_substitution (di, 0);
+ /* The substituted name may have been a template name and
+ may be followed by tepmlate args. */
+ if (d_peek_char (di) == 'I')
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret,
+ d_template_args (di));
+ else
+ can_subst = 0;
+ }
+ else
+ {
+ ret = d_class_enum_type (di);
+ /* If the substitution was a complete type, then it is not
+ a new substitution candidate. However, if the
+ substitution was followed by template arguments, then
+ the whole thing is a substitution candidate. */
+ if (ret != NULL && ret->type == DEMANGLE_COMPONENT_SUB_STD)
+ can_subst = 0;
+ }
+ }
+ break;
+
+ case 'P':
+ d_advance (di, 1);
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_POINTER,
+ cplus_demangle_type (di), NULL);
+ break;
+
+ case 'R':
+ d_advance (di, 1);
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_REFERENCE,
+ cplus_demangle_type (di), NULL);
+ break;
+
+ case 'C':
+ d_advance (di, 1);
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_COMPLEX,
+ cplus_demangle_type (di), NULL);
+ break;
+
+ case 'G':
+ d_advance (di, 1);
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_IMAGINARY,
+ cplus_demangle_type (di), NULL);
+ break;
+
+ case 'U':
+ d_advance (di, 1);
+ ret = d_source_name (di);
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL,
+ cplus_demangle_type (di), ret);
+ break;
+
+ default:
+ return NULL;
+ }
+
+ if (can_subst)
+ {
+ if (! d_add_substitution (di, ret))
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* <CV-qualifiers> ::= [r] [V] [K] */
+
+static struct demangle_component **
+d_cv_qualifiers (di, pret, member_fn)
+ struct d_info *di;
+ struct demangle_component **pret;
+ int member_fn;
+{
+ char peek;
+
+ peek = d_peek_char (di);
+ while (peek == 'r' || peek == 'V' || peek == 'K')
+ {
+ enum demangle_component_type t;
+
+ d_advance (di, 1);
+ if (peek == 'r')
+ {
+ t = (member_fn
+ ? DEMANGLE_COMPONENT_RESTRICT_THIS
+ : DEMANGLE_COMPONENT_RESTRICT);
+ di->expansion += sizeof "restrict";
+ }
+ else if (peek == 'V')
+ {
+ t = (member_fn
+ ? DEMANGLE_COMPONENT_VOLATILE_THIS
+ : DEMANGLE_COMPONENT_VOLATILE);
+ di->expansion += sizeof "volatile";
+ }
+ else
+ {
+ t = (member_fn
+ ? DEMANGLE_COMPONENT_CONST_THIS
+ : DEMANGLE_COMPONENT_CONST);
+ di->expansion += sizeof "const";
+ }
+
+ *pret = d_make_comp (di, t, NULL, NULL);
+ if (*pret == NULL)
+ return NULL;
+ pret = &d_left (*pret);
+
+ peek = d_peek_char (di);
+ }
+
+ return pret;
+}
+
+/* <function-type> ::= F [Y] <bare-function-type> E */
+
+static struct demangle_component *
+d_function_type (di)
+ struct d_info *di;
+{
+ struct demangle_component *ret;
+
+ if (d_next_char (di) != 'F')
+ return NULL;
+ if (d_peek_char (di) == 'Y')
+ {
+ /* Function has C linkage. We don't print this information.
+ FIXME: We should print it in verbose mode. */
+ d_advance (di, 1);
+ }
+ ret = d_bare_function_type (di, 1);
+ if (d_next_char (di) != 'E')
+ return NULL;
+ return ret;
+}
+
+/* <bare-function-type> ::= <type>+ */
+
+static struct demangle_component *
+d_bare_function_type (di, has_return_type)
+ struct d_info *di;
+ int has_return_type;
+{
+ struct demangle_component *return_type;
+ struct demangle_component *tl;
+ struct demangle_component **ptl;
+
+ return_type = NULL;
+ tl = NULL;
+ ptl = &tl;
+ while (1)
+ {
+ char peek;
+ struct demangle_component *type;
+
+ peek = d_peek_char (di);
+ if (peek == '\0' || peek == 'E')
+ break;
+ type = cplus_demangle_type (di);
+ if (type == NULL)
+ return NULL;
+ if (has_return_type)
+ {
+ return_type = type;
+ has_return_type = 0;
+ }
+ else
+ {
+ *ptl = d_make_comp (di, DEMANGLE_COMPONENT_ARGLIST, type, NULL);
+ if (*ptl == NULL)
+ return NULL;
+ ptl = &d_right (*ptl);
+ }
+ }
+
+ /* There should be at least one parameter type besides the optional
+ return type. A function which takes no arguments will have a
+ single parameter type void. */
+ if (tl == NULL)
+ return NULL;
+
+ /* If we have a single parameter type void, omit it. */
+ if (d_right (tl) == NULL
+ && d_left (tl)->type == DEMANGLE_COMPONENT_BUILTIN_TYPE
+ && d_left (tl)->u.s_builtin.type->print == D_PRINT_VOID)
+ {
+ di->expansion -= d_left (tl)->u.s_builtin.type->len;
+ tl = NULL;
+ }
+
+ return d_make_comp (di, DEMANGLE_COMPONENT_FUNCTION_TYPE, return_type, tl);
+}
+
+/* <class-enum-type> ::= <name> */
+
+static struct demangle_component *
+d_class_enum_type (di)
+ struct d_info *di;
+{
+ return d_name (di);
+}
+
+/* <array-type> ::= A <(positive dimension) number> _ <(element) type>
+ ::= A [<(dimension) expression>] _ <(element) type>
+*/
+
+static struct demangle_component *
+d_array_type (di)
+ struct d_info *di;
+{
+ char peek;
+ struct demangle_component *dim;
+
+ if (d_next_char (di) != 'A')
+ return NULL;
+
+ peek = d_peek_char (di);
+ if (peek == '_')
+ dim = NULL;
+ else if (IS_DIGIT (peek))
+ {
+ const char *s;
+
+ s = d_str (di);
+ do
+ {
+ d_advance (di, 1);
+ peek = d_peek_char (di);
+ }
+ while (IS_DIGIT (peek));
+ dim = d_make_name (di, s, d_str (di) - s);
+ if (dim == NULL)
+ return NULL;
+ }
+ else
+ {
+ dim = d_expression (di);
+ if (dim == NULL)
+ return NULL;
+ }
+
+ if (d_next_char (di) != '_')
+ return NULL;
+
+ return d_make_comp (di, DEMANGLE_COMPONENT_ARRAY_TYPE, dim,
+ cplus_demangle_type (di));
+}
+
+/* <pointer-to-member-type> ::= M <(class) type> <(member) type> */
+
+static struct demangle_component *
+d_pointer_to_member_type (di)
+ struct d_info *di;
+{
+ struct demangle_component *cl;
+ struct demangle_component *mem;
+ struct demangle_component **pmem;
+
+ if (d_next_char (di) != 'M')
+ return NULL;
+
+ cl = cplus_demangle_type (di);
+
+ /* The ABI specifies that any type can be a substitution source, and
+ that M is followed by two types, and that when a CV-qualified
+ type is seen both the base type and the CV-qualified types are
+ substitution sources. The ABI also specifies that for a pointer
+ to a CV-qualified member function, the qualifiers are attached to
+ the second type. Given the grammar, a plain reading of the ABI
+ suggests that both the CV-qualified member function and the
+ non-qualified member function are substitution sources. However,
+ g++ does not work that way. g++ treats only the CV-qualified
+ member function as a substitution source. FIXME. So to work
+ with g++, we need to pull off the CV-qualifiers here, in order to
+ avoid calling add_substitution() in cplus_demangle_type(). */
+
+ pmem = d_cv_qualifiers (di, &mem, 1);
+ if (pmem == NULL)
+ return NULL;
+ *pmem = cplus_demangle_type (di);
+
+ return d_make_comp (di, DEMANGLE_COMPONENT_PTRMEM_TYPE, cl, mem);
+}
+
+/* <template-param> ::= T_
+ ::= T <(parameter-2 non-negative) number> _
+*/
+
+static struct demangle_component *
+d_template_param (di)
+ struct d_info *di;
+{
+ long param;
+
+ if (d_next_char (di) != 'T')
+ return NULL;
+
+ if (d_peek_char (di) == '_')
+ param = 0;
+ else
+ {
+ param = d_number (di);
+ if (param < 0)
+ return NULL;
+ param += 1;
+ }
+
+ if (d_next_char (di) != '_')
+ return NULL;
+
+ ++di->did_subs;
+
+ return d_make_template_param (di, param);
+}
+
+/* <template-args> ::= I <template-arg>+ E */
+
+static struct demangle_component *
+d_template_args (di)
+ struct d_info *di;
+{
+ struct demangle_component *hold_last_name;
+ struct demangle_component *al;
+ struct demangle_component **pal;
+
+ /* Preserve the last name we saw--don't let the template arguments
+ clobber it, as that would give us the wrong name for a subsequent
+ constructor or destructor. */
+ hold_last_name = di->last_name;
+
+ if (d_next_char (di) != 'I')
+ return NULL;
+
+ al = NULL;
+ pal = &al;
+ while (1)
+ {
+ struct demangle_component *a;
+
+ a = d_template_arg (di);
+ if (a == NULL)
+ return NULL;
+
+ *pal = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, a, NULL);
+ if (*pal == NULL)
+ return NULL;
+ pal = &d_right (*pal);
+
+ if (d_peek_char (di) == 'E')
+ {
+ d_advance (di, 1);
+ break;
+ }
+ }
+
+ di->last_name = hold_last_name;
+
+ return al;
+}
+
+/* <template-arg> ::= <type>
+ ::= X <expression> E
+ ::= <expr-primary>
+*/
+
+static struct demangle_component *
+d_template_arg (di)
+ struct d_info *di;
+{
+ struct demangle_component *ret;
+
+ switch (d_peek_char (di))
+ {
+ case 'X':
+ d_advance (di, 1);
+ ret = d_expression (di);
+ if (d_next_char (di) != 'E')
+ return NULL;
+ return ret;
+
+ case 'L':
+ return d_expr_primary (di);
+
+ default:
+ return cplus_demangle_type (di);
+ }
+}
+
+/* <expression> ::= <(unary) operator-name> <expression>
+ ::= <(binary) operator-name> <expression> <expression>
+ ::= <(trinary) operator-name> <expression> <expression> <expression>
+ ::= st <type>
+ ::= <template-param>
+ ::= sr <type> <unqualified-name>
+ ::= sr <type> <unqualified-name> <template-args>
+ ::= <expr-primary>
+*/
+
+static struct demangle_component *
+d_expression (di)
+ struct d_info *di;
+{
+ char peek;
+
+ peek = d_peek_char (di);
+ if (peek == 'L')
+ return d_expr_primary (di);
+ else if (peek == 'T')
+ return d_template_param (di);
+ else if (peek == 's' && d_peek_next_char (di) == 'r')
+ {
+ struct demangle_component *type;
+ struct demangle_component *name;
+
+ d_advance (di, 2);
+ type = cplus_demangle_type (di);
+ name = d_unqualified_name (di);
+ if (d_peek_char (di) != 'I')
+ return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type, name);
+ else
+ return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type,
+ d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, name,
+ d_template_args (di)));
+ }
+ else
+ {
+ struct demangle_component *op;
+ int args;
+
+ op = d_operator_name (di);
+ if (op == NULL)
+ return NULL;
+
+ if (op->type == DEMANGLE_COMPONENT_OPERATOR)
+ di->expansion += op->u.s_operator.op->len - 2;
+
+ if (op->type == DEMANGLE_COMPONENT_OPERATOR
+ && strcmp (op->u.s_operator.op->code, "st") == 0)
+ return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op,
+ cplus_demangle_type (di));
+
+ switch (op->type)
+ {
+ default:
+ return NULL;
+ case DEMANGLE_COMPONENT_OPERATOR:
+ args = op->u.s_operator.op->args;
+ break;
+ case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+ args = op->u.s_extended_operator.args;
+ break;
+ case DEMANGLE_COMPONENT_CAST:
+ args = 1;
+ break;
+ }
+
+ switch (args)
+ {
+ case 1:
+ return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op,
+ d_expression (di));
+ case 2:
+ {
+ struct demangle_component *left;
+
+ left = d_expression (di);
+ return d_make_comp (di, DEMANGLE_COMPONENT_BINARY, op,
+ d_make_comp (di,
+ DEMANGLE_COMPONENT_BINARY_ARGS,
+ left,
+ d_expression (di)));
+ }
+ case 3:
+ {
+ struct demangle_component *first;
+ struct demangle_component *second;
+
+ first = d_expression (di);
+ second = d_expression (di);
+ return d_make_comp (di, DEMANGLE_COMPONENT_TRINARY, op,
+ d_make_comp (di,
+ DEMANGLE_COMPONENT_TRINARY_ARG1,
+ first,
+ d_make_comp (di,
+ DEMANGLE_COMPONENT_TRINARY_ARG2,
+ second,
+ d_expression (di))));
+ }
+ default:
+ return NULL;
+ }
+ }
+}
+
+/* <expr-primary> ::= L <type> <(value) number> E
+ ::= L <type> <(value) float> E
+ ::= L <mangled-name> E
+*/
+
+static struct demangle_component *
+d_expr_primary (di)
+ struct d_info *di;
+{
+ struct demangle_component *ret;
+
+ if (d_next_char (di) != 'L')
+ return NULL;
+ if (d_peek_char (di) == '_')
+ ret = cplus_demangle_mangled_name (di, 0);
+ else
+ {
+ struct demangle_component *type;
+ enum demangle_component_type t;
+ const char *s;
+
+ type = cplus_demangle_type (di);
+ if (type == NULL)
+ return NULL;
+
+ /* If we have a type we know how to print, we aren't going to
+ print the type name itself. */
+ if (type->type == DEMANGLE_COMPONENT_BUILTIN_TYPE
+ && type->u.s_builtin.type->print != D_PRINT_DEFAULT)
+ di->expansion -= type->u.s_builtin.type->len;
+
+ /* Rather than try to interpret the literal value, we just
+ collect it as a string. Note that it's possible to have a
+ floating point literal here. The ABI specifies that the
+ format of such literals is machine independent. That's fine,
+ but what's not fine is that versions of g++ up to 3.2 with
+ -fabi-version=1 used upper case letters in the hex constant,
+ and dumped out gcc's internal representation. That makes it
+ hard to tell where the constant ends, and hard to dump the
+ constant in any readable form anyhow. We don't attempt to
+ handle these cases. */
+
+ t = DEMANGLE_COMPONENT_LITERAL;
+ if (d_peek_char (di) == 'n')
+ {
+ t = DEMANGLE_COMPONENT_LITERAL_NEG;
+ d_advance (di, 1);
+ }
+ s = d_str (di);
+ while (d_peek_char (di) != 'E')
+ d_advance (di, 1);
+ ret = d_make_comp (di, t, type, d_make_name (di, s, d_str (di) - s));
+ }
+ if (d_next_char (di) != 'E')
+ return NULL;
+ return ret;
+}
+
+/* <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
+ ::= Z <(function) encoding> E s [<discriminator>]
+*/
+
+static struct demangle_component *
+d_local_name (di)
+ struct d_info *di;
+{
+ struct demangle_component *function;
+
+ if (d_next_char (di) != 'Z')
+ return NULL;
+
+ function = d_encoding (di, 0);
+
+ if (d_next_char (di) != 'E')
+ return NULL;
+
+ if (d_peek_char (di) == 's')
+ {
+ d_advance (di, 1);
+ if (! d_discriminator (di))
+ return NULL;
+ return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function,
+ d_make_name (di, "string literal",
+ sizeof "string literal" - 1));
+ }
+ else
+ {
+ struct demangle_component *name;
+
+ name = d_name (di);
+ if (! d_discriminator (di))
+ return NULL;
+ return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function, name);
+ }
+}
+
+/* <discriminator> ::= _ <(non-negative) number>
+
+ We demangle the discriminator, but we don't print it out. FIXME:
+ We should print it out in verbose mode. */
+
+static int
+d_discriminator (di)
+ struct d_info *di;
+{
+ long discrim;
+
+ if (d_peek_char (di) != '_')
+ return 1;
+ d_advance (di, 1);
+ discrim = d_number (di);
+ if (discrim < 0)
+ return 0;
+ return 1;
+}
+
+/* Add a new substitution. */
+
+static int
+d_add_substitution (di, dc)
+ struct d_info *di;
+ struct demangle_component *dc;
+{
+ if (dc == NULL)
+ return 0;
+ if (di->next_sub >= di->num_subs)
+ return 0;
+ di->subs[di->next_sub] = dc;
+ ++di->next_sub;
+ return 1;
+}
+
+/* <substitution> ::= S <seq-id> _
+ ::= S_
+ ::= St
+ ::= Sa
+ ::= Sb
+ ::= Ss
+ ::= Si
+ ::= So
+ ::= Sd
+
+ If PREFIX is non-zero, then this type is being used as a prefix in
+ a qualified name. In this case, for the standard substitutions, we
+ need to check whether we are being used as a prefix for a
+ constructor or destructor, and return a full template name.
+ Otherwise we will get something like std::iostream::~iostream()
+ which does not correspond particularly well to any function which
+ actually appears in the source.
+*/
+
+static const struct d_standard_sub_info standard_subs[] =
+{
+ { 't', NL ("std"),
+ NL ("std"),
+ NULL, 0 },
+ { 'a', NL ("std::allocator"),
+ NL ("std::allocator"),
+ NL ("allocator") },
+ { 'b', NL ("std::basic_string"),
+ NL ("std::basic_string"),
+ NL ("basic_string") },
+ { 's', NL ("std::string"),
+ NL ("std::basic_string<char, std::char_traits<char>, std::allocator<char> >"),
+ NL ("basic_string") },
+ { 'i', NL ("std::istream"),
+ NL ("std::basic_istream<char, std::char_traits<char> >"),
+ NL ("basic_istream") },
+ { 'o', NL ("std::ostream"),
+ NL ("std::basic_ostream<char, std::char_traits<char> >"),
+ NL ("basic_ostream") },
+ { 'd', NL ("std::iostream"),
+ NL ("std::basic_iostream<char, std::char_traits<char> >"),
+ NL ("basic_iostream") }
+};
+
+static struct demangle_component *
+d_substitution (di, prefix)
+ struct d_info *di;
+ int prefix;
+{
+ char c;
+
+ if (d_next_char (di) != 'S')
+ return NULL;
+
+ c = d_next_char (di);
+ if (c == '_' || IS_DIGIT (c) || IS_UPPER (c))
+ {
+ int id;
+
+ id = 0;
+ if (c != '_')
+ {
+ do
+ {
+ if (IS_DIGIT (c))
+ id = id * 36 + c - '0';
+ else if (IS_UPPER (c))
+ id = id * 36 + c - 'A' + 10;
+ else
+ return NULL;
+ c = d_next_char (di);
+ }
+ while (c != '_');
+
+ ++id;
+ }
+
+ if (id >= di->next_sub)
+ return NULL;
+
+ ++di->did_subs;
+
+ return di->subs[id];
+ }
+ else
+ {
+ int verbose;
+ const struct d_standard_sub_info *p;
+ const struct d_standard_sub_info *pend;
+
+ verbose = (di->options & DMGL_VERBOSE) != 0;
+ if (! verbose && prefix)
+ {
+ char peek;
+
+ peek = d_peek_char (di);
+ if (peek == 'C' || peek == 'D')
+ verbose = 1;
+ }
+
+ pend = (&standard_subs[0]
+ + sizeof standard_subs / sizeof standard_subs[0]);
+ for (p = &standard_subs[0]; p < pend; ++p)
+ {
+ if (c == p->code)
+ {
+ const char *s;
+ int len;
+
+ if (p->set_last_name != NULL)
+ di->last_name = d_make_sub (di, p->set_last_name,
+ p->set_last_name_len);
+ if (verbose)
+ {
+ s = p->full_expansion;
+ len = p->full_len;
+ }
+ else
+ {
+ s = p->simple_expansion;
+ len = p->simple_len;
+ }
+ di->expansion += len;
+ return d_make_sub (di, s, len);
+ }
+ }
+
+ return NULL;
+ }
+}
+
+/* Resize the print buffer. */
+
+static void
+d_print_resize (dpi, add)
+ struct d_print_info *dpi;
+ size_t add;
+{
+ size_t need;
+
+ if (dpi->buf == NULL)
+ return;
+ need = dpi->len + add;
+ while (need > dpi->alc)
+ {
+ size_t newalc;
+ char *newbuf;
+
+ newalc = dpi->alc * 2;
+ newbuf = realloc (dpi->buf, newalc);
+ if (newbuf == NULL)
+ {
+ free (dpi->buf);
+ dpi->buf = NULL;
+ dpi->allocation_failure = 1;
+ return;
+ }
+ dpi->buf = newbuf;
+ dpi->alc = newalc;
+ }
+}
+
+/* Append a character to the print buffer. */
+
+static void
+d_print_append_char (dpi, c)
+ struct d_print_info *dpi;
+ int c;
+{
+ if (dpi->buf != NULL)
+ {
+ if (dpi->len >= dpi->alc)
+ {
+ d_print_resize (dpi, 1);
+ if (dpi->buf == NULL)
+ return;
+ }
+
+ dpi->buf[dpi->len] = c;
+ ++dpi->len;
+ }
+}
+
+/* Append a buffer to the print buffer. */
+
+static void
+d_print_append_buffer (dpi, s, l)
+ struct d_print_info *dpi;
+ const char *s;
+ size_t l;
+{
+ if (dpi->buf != NULL)
+ {
+ if (dpi->len + l > dpi->alc)
+ {
+ d_print_resize (dpi, l);
+ if (dpi->buf == NULL)
+ return;
+ }
+
+ memcpy (dpi->buf + dpi->len, s, l);
+ dpi->len += l;
+ }
+}
+
+/* Indicate that an error occurred during printing. */
+
+static void
+d_print_error (dpi)
+ struct d_print_info *dpi;
+{
+ free (dpi->buf);
+ dpi->buf = NULL;
+}
+
+/* Turn components into a human readable string. OPTIONS is the
+ options bits passed to the demangler. DC is the tree to print.
+ ESTIMATE is a guess at the length of the result. This returns a
+ string allocated by malloc, or NULL on error. On success, this
+ sets *PALC to the size of the allocated buffer. On failure, this
+ sets *PALC to 0 for a bad parse, or to 1 for a memory allocation
+ failure. */
+
+CP_STATIC_IF_GLIBCPP_V3
+char *
+cplus_demangle_print (options, dc, estimate, palc)
+ int options;
+ const struct demangle_component *dc;
+ int estimate;
+ size_t *palc;
+{
+ struct d_print_info dpi;
+
+ dpi.options = options;
+
+ dpi.alc = estimate + 1;
+ dpi.buf = malloc (dpi.alc);
+ if (dpi.buf == NULL)
+ {
+ *palc = 1;
+ return NULL;
+ }
+
+ dpi.len = 0;
+ dpi.templates = NULL;
+ dpi.modifiers = NULL;
+
+ dpi.allocation_failure = 0;
+
+ d_print_comp (&dpi, dc);
+
+ d_append_char (&dpi, '\0');
+
+ if (dpi.buf != NULL)
+ *palc = dpi.alc;
+ else
+ *palc = dpi.allocation_failure;
+
+ return dpi.buf;
+}
+
+/* Subroutine to handle components. */
+
+static void
+d_print_comp (dpi, dc)
+ struct d_print_info *dpi;
+ const struct demangle_component *dc;
+{
+ if (dc == NULL)
+ {
+ d_print_error (dpi);
+ return;
+ }
+ if (d_print_saw_error (dpi))
+ return;
+
+ switch (dc->type)
+ {
+ case DEMANGLE_COMPONENT_NAME:
+ if ((dpi->options & DMGL_JAVA) == 0)
+ d_append_buffer (dpi, dc->u.s_name.s, dc->u.s_name.len);
+ else
+ d_print_java_identifier (dpi, dc->u.s_name.s, dc->u.s_name.len);
+ return;
+
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ d_print_comp (dpi, d_left (dc));
+ if ((dpi->options & DMGL_JAVA) == 0)
+ d_append_string_constant (dpi, "::");
+ else
+ d_append_char (dpi, '.');
+ d_print_comp (dpi, d_right (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_TYPED_NAME:
+ {
+ struct d_print_mod *hold_modifiers;
+ struct demangle_component *typed_name;
+ struct d_print_mod adpm[4];
+ unsigned int i;
+ struct d_print_template dpt;
+
+ /* Pass the name down to the type so that it can be printed in
+ the right place for the type. We also have to pass down
+ any CV-qualifiers, which apply to the this parameter. */
+ hold_modifiers = dpi->modifiers;
+ i = 0;
+ typed_name = d_left (dc);
+ while (typed_name != NULL)
+ {
+ if (i >= sizeof adpm / sizeof adpm[0])
+ {
+ d_print_error (dpi);
+ return;
+ }
+
+ adpm[i].next = dpi->modifiers;
+ dpi->modifiers = &adpm[i];
+ adpm[i].mod = typed_name;
+ adpm[i].printed = 0;
+ adpm[i].templates = dpi->templates;
+ ++i;
+
+ if (typed_name->type != DEMANGLE_COMPONENT_RESTRICT_THIS
+ && typed_name->type != DEMANGLE_COMPONENT_VOLATILE_THIS
+ && typed_name->type != DEMANGLE_COMPONENT_CONST_THIS)
+ break;
+
+ typed_name = d_left (typed_name);
+ }
+
+ /* If typed_name is a template, then it applies to the
+ function type as well. */
+ if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE)
+ {
+ dpt.next = dpi->templates;
+ dpi->templates = &dpt;
+ dpt.template = typed_name;
+ }
+
+ /* If typed_name is a DEMANGLE_COMPONENT_LOCAL_NAME, then
+ there may be CV-qualifiers on its right argument which
+ really apply here; this happens when parsing a class which
+ is local to a function. */
+ if (typed_name->type == DEMANGLE_COMPONENT_LOCAL_NAME)
+ {
+ struct demangle_component *local_name;
+
+ local_name = d_right (typed_name);
+ while (local_name->type == DEMANGLE_COMPONENT_RESTRICT_THIS
+ || local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS
+ || local_name->type == DEMANGLE_COMPONENT_CONST_THIS)
+ {
+ if (i >= sizeof adpm / sizeof adpm[0])
+ {
+ d_print_error (dpi);
+ return;
+ }
+
+ adpm[i] = adpm[i - 1];
+ adpm[i].next = &adpm[i - 1];
+ dpi->modifiers = &adpm[i];
+
+ adpm[i - 1].mod = local_name;
+ adpm[i - 1].printed = 0;
+ adpm[i - 1].templates = dpi->templates;
+ ++i;
+
+ local_name = d_left (local_name);
+ }
+ }
+
+ d_print_comp (dpi, d_right (dc));
+
+ if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE)
+ dpi->templates = dpt.next;
+
+ /* If the modifiers didn't get printed by the type, print them
+ now. */
+ while (i > 0)
+ {
+ --i;
+ if (! adpm[i].printed)
+ {
+ d_append_char (dpi, ' ');
+ d_print_mod (dpi, adpm[i].mod);
+ }
+ }
+
+ dpi->modifiers = hold_modifiers;
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ {
+ struct d_print_mod *hold_dpm;
+
+ /* Don't push modifiers into a template definition. Doing so
+ could give the wrong definition for a template argument.
+ Instead, treat the template essentially as a name. */
+
+ hold_dpm = dpi->modifiers;
+ dpi->modifiers = NULL;
+
+ d_print_comp (dpi, d_left (dc));
+ if (d_last_char (dpi) == '<')
+ d_append_char (dpi, ' ');
+ d_append_char (dpi, '<');
+ d_print_comp (dpi, d_right (dc));
+ /* Avoid generating two consecutive '>' characters, to avoid
+ the C++ syntactic ambiguity. */
+ if (d_last_char (dpi) == '>')
+ d_append_char (dpi, ' ');
+ d_append_char (dpi, '>');
+
+ dpi->modifiers = hold_dpm;
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
+ {
+ long i;
+ struct demangle_component *a;
+ struct d_print_template *hold_dpt;
+
+ if (dpi->templates == NULL)
+ {
+ d_print_error (dpi);
+ return;
+ }
+ i = dc->u.s_number.number;
+ for (a = d_right (dpi->templates->template);
+ a != NULL;
+ a = d_right (a))
+ {
+ if (a->type != DEMANGLE_COMPONENT_TEMPLATE_ARGLIST)
+ {
+ d_print_error (dpi);
+ return;
+ }
+ if (i <= 0)
+ break;
+ --i;
+ }
+ if (i != 0 || a == NULL)
+ {
+ d_print_error (dpi);
+ return;
+ }
+
+ /* While processing this parameter, we need to pop the list of
+ templates. This is because the template parameter may
+ itself be a reference to a parameter of an outer
+ template. */
+
+ hold_dpt = dpi->templates;
+ dpi->templates = hold_dpt->next;
+
+ d_print_comp (dpi, d_left (a));
+
+ dpi->templates = hold_dpt;
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_CTOR:
+ d_print_comp (dpi, dc->u.s_ctor.name);
+ return;
+
+ case DEMANGLE_COMPONENT_DTOR:
+ d_append_char (dpi, '~');
+ d_print_comp (dpi, dc->u.s_dtor.name);
+ return;
+
+ case DEMANGLE_COMPONENT_VTABLE:
+ d_append_string_constant (dpi, "vtable for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_VTT:
+ d_append_string_constant (dpi, "VTT for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE:
+ d_append_string_constant (dpi, "construction vtable for ");
+ d_print_comp (dpi, d_left (dc));
+ d_append_string_constant (dpi, "-in-");
+ d_print_comp (dpi, d_right (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_TYPEINFO:
+ d_append_string_constant (dpi, "typeinfo for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_TYPEINFO_NAME:
+ d_append_string_constant (dpi, "typeinfo name for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_TYPEINFO_FN:
+ d_append_string_constant (dpi, "typeinfo fn for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_THUNK:
+ d_append_string_constant (dpi, "non-virtual thunk to ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_VIRTUAL_THUNK:
+ d_append_string_constant (dpi, "virtual thunk to ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_COVARIANT_THUNK:
+ d_append_string_constant (dpi, "covariant return thunk to ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_JAVA_CLASS:
+ d_append_string_constant (dpi, "java Class for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_GUARD:
+ d_append_string_constant (dpi, "guard variable for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_REFTEMP:
+ d_append_string_constant (dpi, "reference temporary for ");
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_SUB_STD:
+ d_append_buffer (dpi, dc->u.s_string.string, dc->u.s_string.len);
+ return;
+
+ case DEMANGLE_COMPONENT_RESTRICT:
+ case DEMANGLE_COMPONENT_VOLATILE:
+ case DEMANGLE_COMPONENT_CONST:
+ {
+ struct d_print_mod *pdpm;
+
+ /* When printing arrays, it's possible to have cases where the
+ same CV-qualifier gets pushed on the stack multiple times.
+ We only need to print it once. */
+
+ for (pdpm = dpi->modifiers; pdpm != NULL; pdpm = pdpm->next)
+ {
+ if (! pdpm->printed)
+ {
+ if (pdpm->mod->type != DEMANGLE_COMPONENT_RESTRICT
+ && pdpm->mod->type != DEMANGLE_COMPONENT_VOLATILE
+ && pdpm->mod->type != DEMANGLE_COMPONENT_CONST)
+ break;
+ if (pdpm->mod->type == dc->type)
+ {
+ d_print_comp (dpi, d_left (dc));
+ return;
+ }
+ }
+ }
+ }
+ /* Fall through. */
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+ case DEMANGLE_COMPONENT_POINTER:
+ case DEMANGLE_COMPONENT_REFERENCE:
+ case DEMANGLE_COMPONENT_COMPLEX:
+ case DEMANGLE_COMPONENT_IMAGINARY:
+ {
+ /* We keep a list of modifiers on the stack. */
+ struct d_print_mod dpm;
+
+ dpm.next = dpi->modifiers;
+ dpi->modifiers = &dpm;
+ dpm.mod = dc;
+ dpm.printed = 0;
+ dpm.templates = dpi->templates;
+
+ d_print_comp (dpi, d_left (dc));
+
+ /* If the modifier didn't get printed by the type, print it
+ now. */
+ if (! dpm.printed)
+ d_print_mod (dpi, dc);
+
+ dpi->modifiers = dpm.next;
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_BUILTIN_TYPE:
+ if ((dpi->options & DMGL_JAVA) == 0)
+ d_append_buffer (dpi, dc->u.s_builtin.type->name,
+ dc->u.s_builtin.type->len);
+ else
+ d_append_buffer (dpi, dc->u.s_builtin.type->java_name,
+ dc->u.s_builtin.type->java_len);
+ return;
+
+ case DEMANGLE_COMPONENT_VENDOR_TYPE:
+ d_print_comp (dpi, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_FUNCTION_TYPE:
+ {
+ if (d_left (dc) != NULL)
+ {
+ struct d_print_mod dpm;
+
+ /* We must pass this type down as a modifier in order to
+ print it in the right location. */
+
+ dpm.next = dpi->modifiers;
+ dpi->modifiers = &dpm;
+ dpm.mod = dc;
+ dpm.printed = 0;
+ dpm.templates = dpi->templates;
+
+ d_print_comp (dpi, d_left (dc));
+
+ dpi->modifiers = dpm.next;
+
+ if (dpm.printed)
+ return;
+
+ d_append_char (dpi, ' ');
+ }
+
+ d_print_function_type (dpi, dc, dpi->modifiers);
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_ARRAY_TYPE:
+ {
+ struct d_print_mod *hold_modifiers;
+ struct d_print_mod adpm[4];
+ unsigned int i;
+ struct d_print_mod *pdpm;
+
+ /* We must pass this type down as a modifier in order to print
+ multi-dimensional arrays correctly. If the array itself is
+ CV-qualified, we act as though the element type were
+ CV-qualified. We do this by copying the modifiers down
+ rather than fiddling pointers, so that we don't wind up
+ with a d_print_mod higher on the stack pointing into our
+ stack frame after we return. */
+
+ hold_modifiers = dpi->modifiers;
+
+ adpm[0].next = hold_modifiers;
+ dpi->modifiers = &adpm[0];
+ adpm[0].mod = dc;
+ adpm[0].printed = 0;
+ adpm[0].templates = dpi->templates;
+
+ i = 1;
+ pdpm = hold_modifiers;
+ while (pdpm != NULL
+ && (pdpm->mod->type == DEMANGLE_COMPONENT_RESTRICT
+ || pdpm->mod->type == DEMANGLE_COMPONENT_VOLATILE
+ || pdpm->mod->type == DEMANGLE_COMPONENT_CONST))
+ {
+ if (! pdpm->printed)
+ {
+ if (i >= sizeof adpm / sizeof adpm[0])
+ {
+ d_print_error (dpi);
+ return;
+ }
+
+ adpm[i] = *pdpm;
+ adpm[i].next = dpi->modifiers;
+ dpi->modifiers = &adpm[i];
+ pdpm->printed = 1;
+ ++i;
+ }
+
+ pdpm = pdpm->next;
+ }
+
+ d_print_comp (dpi, d_right (dc));
+
+ dpi->modifiers = hold_modifiers;
+
+ if (adpm[0].printed)
+ return;
+
+ while (i > 1)
+ {
+ --i;
+ d_print_mod (dpi, adpm[i].mod);
+ }
+
+ d_print_array_type (dpi, dc, dpi->modifiers);
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_PTRMEM_TYPE:
+ {
+ struct d_print_mod dpm;
+
+ dpm.next = dpi->modifiers;
+ dpi->modifiers = &dpm;
+ dpm.mod = dc;
+ dpm.printed = 0;
+ dpm.templates = dpi->templates;
+
+ d_print_comp (dpi, d_right (dc));
+
+ /* If the modifier didn't get printed by the type, print it
+ now. */
+ if (! dpm.printed)
+ {
+ d_append_char (dpi, ' ');
+ d_print_comp (dpi, d_left (dc));
+ d_append_string_constant (dpi, "::*");
+ }
+
+ dpi->modifiers = dpm.next;
+
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_ARGLIST:
+ case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+ d_print_comp (dpi, d_left (dc));
+ if (d_right (dc) != NULL)
+ {
+ d_append_string_constant (dpi, ", ");
+ d_print_comp (dpi, d_right (dc));
+ }
+ return;
+
+ case DEMANGLE_COMPONENT_OPERATOR:
+ {
+ char c;
+
+ d_append_string_constant (dpi, "operator");
+ c = dc->u.s_operator.op->name[0];
+ if (IS_LOWER (c))
+ d_append_char (dpi, ' ');
+ d_append_buffer (dpi, dc->u.s_operator.op->name,
+ dc->u.s_operator.op->len);
+ return;
+ }
+
+ case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+ d_append_string_constant (dpi, "operator ");
+ d_print_comp (dpi, dc->u.s_extended_operator.name);
+ return;
+
+ case DEMANGLE_COMPONENT_CAST:
+ d_append_string_constant (dpi, "operator ");
+ d_print_cast (dpi, dc);
+ return;
+
+ case DEMANGLE_COMPONENT_UNARY:
+ if (d_left (dc)->type != DEMANGLE_COMPONENT_CAST)
+ d_print_expr_op (dpi, d_left (dc));
+ else
+ {
+ d_append_char (dpi, '(');
+ d_print_cast (dpi, d_left (dc));
+ d_append_char (dpi, ')');
+ }
+ d_append_char (dpi, '(');
+ d_print_comp (dpi, d_right (dc));
+ d_append_char (dpi, ')');
+ return;
+
+ case DEMANGLE_COMPONENT_BINARY:
+ if (d_right (dc)->type != DEMANGLE_COMPONENT_BINARY_ARGS)
+ {
+ d_print_error (dpi);
+ return;
+ }
+
+ /* We wrap an expression which uses the greater-than operator in
+ an extra layer of parens so that it does not get confused
+ with the '>' which ends the template parameters. */
+ if (d_left (dc)->type == DEMANGLE_COMPONENT_OPERATOR
+ && d_left (dc)->u.s_operator.op->len == 1
+ && d_left (dc)->u.s_operator.op->name[0] == '>')
+ d_append_char (dpi, '(');
+
+ d_append_char (dpi, '(');
+ d_print_comp (dpi, d_left (d_right (dc)));
+ d_append_string_constant (dpi, ") ");
+ d_print_expr_op (dpi, d_left (dc));
+ d_append_string_constant (dpi, " (");
+ d_print_comp (dpi, d_right (d_right (dc)));
+ d_append_char (dpi, ')');
+
+ if (d_left (dc)->type == DEMANGLE_COMPONENT_OPERATOR
+ && d_left (dc)->u.s_operator.op->len == 1
+ && d_left (dc)->u.s_operator.op->name[0] == '>')
+ d_append_char (dpi, ')');
+
+ return;
+
+ case DEMANGLE_COMPONENT_BINARY_ARGS:
+ /* We should only see this as part of DEMANGLE_COMPONENT_BINARY. */
+ d_print_error (dpi);
+ return;
+
+ case DEMANGLE_COMPONENT_TRINARY:
+ if (d_right (dc)->type != DEMANGLE_COMPONENT_TRINARY_ARG1
+ || d_right (d_right (dc))->type != DEMANGLE_COMPONENT_TRINARY_ARG2)
+ {
+ d_print_error (dpi);
+ return;
+ }
+ d_append_char (dpi, '(');
+ d_print_comp (dpi, d_left (d_right (dc)));
+ d_append_string_constant (dpi, ") ");
+ d_print_expr_op (dpi, d_left (dc));
+ d_append_string_constant (dpi, " (");
+ d_print_comp (dpi, d_left (d_right (d_right (dc))));
+ d_append_string_constant (dpi, ") : (");
+ d_print_comp (dpi, d_right (d_right (d_right (dc))));
+ d_append_char (dpi, ')');
+ return;
+
+ case DEMANGLE_COMPONENT_TRINARY_ARG1:
+ case DEMANGLE_COMPONENT_TRINARY_ARG2:
+ /* We should only see these are part of DEMANGLE_COMPONENT_TRINARY. */
+ d_print_error (dpi);
+ return;
+
+ case DEMANGLE_COMPONENT_LITERAL:
+ case DEMANGLE_COMPONENT_LITERAL_NEG:
+ {
+ enum d_builtin_type_print tp;
+
+ /* For some builtin types, produce simpler output. */
+ tp = D_PRINT_DEFAULT;
+ if (d_left (dc)->type == DEMANGLE_COMPONENT_BUILTIN_TYPE)
+ {
+ tp = d_left (dc)->u.s_builtin.type->print;
+ switch (tp)
+ {
+ case D_PRINT_INT:
+ case D_PRINT_UNSIGNED:
+ case D_PRINT_LONG:
+ case D_PRINT_UNSIGNED_LONG:
+ case D_PRINT_LONG_LONG:
+ case D_PRINT_UNSIGNED_LONG_LONG:
+ if (d_right (dc)->type == DEMANGLE_COMPONENT_NAME)
+ {
+ if (dc->type == DEMANGLE_COMPONENT_LITERAL_NEG)
+ d_append_char (dpi, '-');
+ d_print_comp (dpi, d_right (dc));
+ switch (tp)
+ {
+ default:
+ break;
+ case D_PRINT_UNSIGNED:
+ d_append_char (dpi, 'u');
+ break;
+ case D_PRINT_LONG:
+ d_append_char (dpi, 'l');
+ break;
+ case D_PRINT_UNSIGNED_LONG:
+ d_append_string_constant (dpi, "ul");
+ break;
+ case D_PRINT_LONG_LONG:
+ d_append_string_constant (dpi, "ll");
+ break;
+ case D_PRINT_UNSIGNED_LONG_LONG:
+ d_append_string_constant (dpi, "ull");
+ break;
+ }
+ return;
+ }
+ break;
+
+ case D_PRINT_BOOL:
+ if (d_right (dc)->type == DEMANGLE_COMPONENT_NAME
+ && d_right (dc)->u.s_name.len == 1
+ && dc->type == DEMANGLE_COMPONENT_LITERAL)
+ {
+ switch (d_right (dc)->u.s_name.s[0])
+ {
+ case '0':
+ d_append_string_constant (dpi, "false");
+ return;
+ case '1':
+ d_append_string_constant (dpi, "true");
+ return;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ d_append_char (dpi, '(');
+ d_print_comp (dpi, d_left (dc));
+ d_append_char (dpi, ')');
+ if (dc->type == DEMANGLE_COMPONENT_LITERAL_NEG)
+ d_append_char (dpi, '-');
+ if (tp == D_PRINT_FLOAT)
+ d_append_char (dpi, '[');
+ d_print_comp (dpi, d_right (dc));
+ if (tp == D_PRINT_FLOAT)
+ d_append_char (dpi, ']');
+ }
+ return;
+
+ default:
+ d_print_error (dpi);
+ return;
+ }
+}
+
+/* Print a Java dentifier. For Java we try to handle encoded extended
+ Unicode characters. The C++ ABI doesn't mention Unicode encoding,
+ so we don't it for C++. Characters are encoded as
+ __U<hex-char>+_. */
+
+static void
+d_print_java_identifier (dpi, name, len)
+ struct d_print_info *dpi;
+ const char *name;
+ int len;
+{
+ const char *p;
+ const char *end;
+
+ end = name + len;
+ for (p = name; p < end; ++p)
+ {
+ if (end - p > 3
+ && p[0] == '_'
+ && p[1] == '_'
+ && p[2] == 'U')
+ {
+ unsigned long c;
+ const char *q;
+
+ c = 0;
+ for (q = p + 3; q < end; ++q)
+ {
+ int dig;
+
+ if (IS_DIGIT (*q))
+ dig = *q - '0';
+ else if (*q >= 'A' && *q <= 'F')
+ dig = *q - 'A' + 10;
+ else if (*q >= 'a' && *q <= 'f')
+ dig = *q - 'a' + 10;
+ else
+ break;
+
+ c = c * 16 + dig;
+ }
+ /* If the Unicode character is larger than 256, we don't try
+ to deal with it here. FIXME. */
+ if (q < end && *q == '_' && c < 256)
+ {
+ d_append_char (dpi, c);
+ p = q;
+ continue;
+ }
+ }
+
+ d_append_char (dpi, *p);
+ }
+}
+
+/* Print a list of modifiers. SUFFIX is 1 if we are printing
+ qualifiers on this after printing a function. */
+
+static void
+d_print_mod_list (dpi, mods, suffix)
+ struct d_print_info *dpi;
+ struct d_print_mod *mods;
+ int suffix;
+{
+ struct d_print_template *hold_dpt;
+
+ if (mods == NULL || d_print_saw_error (dpi))
+ return;
+
+ if (mods->printed
+ || (! suffix
+ && (mods->mod->type == DEMANGLE_COMPONENT_RESTRICT_THIS
+ || mods->mod->type == DEMANGLE_COMPONENT_VOLATILE_THIS
+ || mods->mod->type == DEMANGLE_COMPONENT_CONST_THIS)))
+ {
+ d_print_mod_list (dpi, mods->next, suffix);
+ return;
+ }
+
+ mods->printed = 1;
+
+ hold_dpt = dpi->templates;
+ dpi->templates = mods->templates;
+
+ if (mods->mod->type == DEMANGLE_COMPONENT_FUNCTION_TYPE)
+ {
+ d_print_function_type (dpi, mods->mod, mods->next);
+ dpi->templates = hold_dpt;
+ return;
+ }
+ else if (mods->mod->type == DEMANGLE_COMPONENT_ARRAY_TYPE)
+ {
+ d_print_array_type (dpi, mods->mod, mods->next);
+ dpi->templates = hold_dpt;
+ return;
+ }
+ else if (mods->mod->type == DEMANGLE_COMPONENT_LOCAL_NAME)
+ {
+ struct d_print_mod *hold_modifiers;
+ struct demangle_component *dc;
+
+ /* When this is on the modifier stack, we have pulled any
+ qualifiers off the right argument already. Otherwise, we
+ print it as usual, but don't let the left argument see any
+ modifiers. */
+
+ hold_modifiers = dpi->modifiers;
+ dpi->modifiers = NULL;
+ d_print_comp (dpi, d_left (mods->mod));
+ dpi->modifiers = hold_modifiers;
+
+ if ((dpi->options & DMGL_JAVA) == 0)
+ d_append_string_constant (dpi, "::");
+ else
+ d_append_char (dpi, '.');
+
+ dc = d_right (mods->mod);
+ while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS
+ || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
+ || dc->type == DEMANGLE_COMPONENT_CONST_THIS)
+ dc = d_left (dc);
+
+ d_print_comp (dpi, dc);
+
+ dpi->templates = hold_dpt;
+ return;
+ }
+
+ d_print_mod (dpi, mods->mod);
+
+ dpi->templates = hold_dpt;
+
+ d_print_mod_list (dpi, mods->next, suffix);
+}
+
+/* Print a modifier. */
+
+static void
+d_print_mod (dpi, mod)
+ struct d_print_info *dpi;
+ const struct demangle_component *mod;
+{
+ switch (mod->type)
+ {
+ case DEMANGLE_COMPONENT_RESTRICT:
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ d_append_string_constant (dpi, " restrict");
+ return;
+ case DEMANGLE_COMPONENT_VOLATILE:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ d_append_string_constant (dpi, " volatile");
+ return;
+ case DEMANGLE_COMPONENT_CONST:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ d_append_string_constant (dpi, " const");
+ return;
+ case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+ d_append_char (dpi, ' ');
+ d_print_comp (dpi, d_right (mod));
+ return;
+ case DEMANGLE_COMPONENT_POINTER:
+ /* There is no pointer symbol in Java. */
+ if ((dpi->options & DMGL_JAVA) == 0)
+ d_append_char (dpi, '*');
+ return;
+ case DEMANGLE_COMPONENT_REFERENCE:
+ d_append_char (dpi, '&');
+ return;
+ case DEMANGLE_COMPONENT_COMPLEX:
+ d_append_string_constant (dpi, "complex ");
+ return;
+ case DEMANGLE_COMPONENT_IMAGINARY:
+ d_append_string_constant (dpi, "imaginary ");
+ return;
+ case DEMANGLE_COMPONENT_PTRMEM_TYPE:
+ if (d_last_char (dpi) != '(')
+ d_append_char (dpi, ' ');
+ d_print_comp (dpi, d_left (mod));
+ d_append_string_constant (dpi, "::*");
+ return;
+ case DEMANGLE_COMPONENT_TYPED_NAME:
+ d_print_comp (dpi, d_left (mod));
+ return;
+ default:
+ /* Otherwise, we have something that won't go back on the
+ modifier stack, so we can just print it. */
+ d_print_comp (dpi, mod);
+ return;
+ }
+}
+
+/* Print a function type, except for the return type. */
+
+static void
+d_print_function_type (dpi, dc, mods)
+ struct d_print_info *dpi;
+ const struct demangle_component *dc;
+ struct d_print_mod *mods;
+{
+ int need_paren;
+ int saw_mod;
+ int need_space;
+ struct d_print_mod *p;
+ struct d_print_mod *hold_modifiers;
+
+ need_paren = 0;
+ saw_mod = 0;
+ need_space = 0;
+ for (p = mods; p != NULL; p = p->next)
+ {
+ if (p->printed)
+ break;
+
+ saw_mod = 1;
+ switch (p->mod->type)
+ {
+ case DEMANGLE_COMPONENT_POINTER:
+ case DEMANGLE_COMPONENT_REFERENCE:
+ need_paren = 1;
+ break;
+ case DEMANGLE_COMPONENT_RESTRICT:
+ case DEMANGLE_COMPONENT_VOLATILE:
+ case DEMANGLE_COMPONENT_CONST:
+ case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+ case DEMANGLE_COMPONENT_COMPLEX:
+ case DEMANGLE_COMPONENT_IMAGINARY:
+ case DEMANGLE_COMPONENT_PTRMEM_TYPE:
+ need_space = 1;
+ need_paren = 1;
+ break;
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ break;
+ default:
+ break;
+ }
+ if (need_paren)
+ break;
+ }
+
+ if (d_left (dc) != NULL && ! saw_mod)
+ need_paren = 1;
+
+ if (need_paren)
+ {
+ if (! need_space)
+ {
+ if (d_last_char (dpi) != '('
+ && d_last_char (dpi) != '*')
+ need_space = 1;
+ }
+ if (need_space && d_last_char (dpi) != ' ')
+ d_append_char (dpi, ' ');
+ d_append_char (dpi, '(');
+ }
+
+ hold_modifiers = dpi->modifiers;
+ dpi->modifiers = NULL;
+
+ d_print_mod_list (dpi, mods, 0);
+
+ if (need_paren)
+ d_append_char (dpi, ')');
+
+ d_append_char (dpi, '(');
+
+ if (d_right (dc) != NULL)
+ d_print_comp (dpi, d_right (dc));
+
+ d_append_char (dpi, ')');
+
+ d_print_mod_list (dpi, mods, 1);
+
+ dpi->modifiers = hold_modifiers;
+}
+
+/* Print an array type, except for the element type. */
+
+static void
+d_print_array_type (dpi, dc, mods)
+ struct d_print_info *dpi;
+ const struct demangle_component *dc;
+ struct d_print_mod *mods;
+{
+ int need_space;
+
+ need_space = 1;
+ if (mods != NULL)
+ {
+ int need_paren;
+ struct d_print_mod *p;
+
+ need_paren = 0;
+ for (p = mods; p != NULL; p = p->next)
+ {
+ if (! p->printed)
+ {
+ if (p->mod->type == DEMANGLE_COMPONENT_ARRAY_TYPE)
+ {
+ need_space = 0;
+ break;
+ }
+ else
+ {
+ need_paren = 1;
+ need_space = 1;
+ break;
+ }
+ }
+ }
+
+ if (need_paren)
+ d_append_string_constant (dpi, " (");
+
+ d_print_mod_list (dpi, mods, 0);
+
+ if (need_paren)
+ d_append_char (dpi, ')');
+ }
+
+ if (need_space)
+ d_append_char (dpi, ' ');
+
+ d_append_char (dpi, '[');
+
+ if (d_left (dc) != NULL)
+ d_print_comp (dpi, d_left (dc));
+
+ d_append_char (dpi, ']');
+}
+
+/* Print an operator in an expression. */
+
+static void
+d_print_expr_op (dpi, dc)
+ struct d_print_info *dpi;
+ const struct demangle_component *dc;
+{
+ if (dc->type == DEMANGLE_COMPONENT_OPERATOR)
+ d_append_buffer (dpi, dc->u.s_operator.op->name,
+ dc->u.s_operator.op->len);
+ else
+ d_print_comp (dpi, dc);
+}
+
+/* Print a cast. */
+
+static void
+d_print_cast (dpi, dc)
+ struct d_print_info *dpi;
+ const struct demangle_component *dc;
+{
+ if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE)
+ d_print_comp (dpi, d_left (dc));
+ else
+ {
+ struct d_print_mod *hold_dpm;
+ struct d_print_template dpt;
+
+ /* It appears that for a templated cast operator, we need to put
+ the template parameters in scope for the operator name, but
+ not for the parameters. The effect is that we need to handle
+ the template printing here. */
+
+ hold_dpm = dpi->modifiers;
+ dpi->modifiers = NULL;
+
+ dpt.next = dpi->templates;
+ dpi->templates = &dpt;
+ dpt.template = d_left (dc);
+
+ d_print_comp (dpi, d_left (d_left (dc)));
+
+ dpi->templates = dpt.next;
+
+ if (d_last_char (dpi) == '<')
+ d_append_char (dpi, ' ');
+ d_append_char (dpi, '<');
+ d_print_comp (dpi, d_right (d_left (dc)));
+ /* Avoid generating two consecutive '>' characters, to avoid
+ the C++ syntactic ambiguity. */
+ if (d_last_char (dpi) == '>')
+ d_append_char (dpi, ' ');
+ d_append_char (dpi, '>');
+
+ dpi->modifiers = hold_dpm;
+ }
+}
+
+/* Initialize the information structure we use to pass around
+ information. */
+
+CP_STATIC_IF_GLIBCPP_V3
+void
+cplus_demangle_init_info (mangled, options, len, di)
+ const char *mangled;
+ int options;
+ size_t len;
+ struct d_info *di;
+{
+ di->s = mangled;
+ di->send = mangled + len;
+ di->options = options;
+
+ di->n = mangled;
+
+ /* We can not need more components than twice the number of chars in
+ the mangled string. Most components correspond directly to
+ chars, but the ARGLIST types are exceptions. */
+ di->num_comps = 2 * len;
+ di->next_comp = 0;
+
+ /* Similarly, we can not need more substitutions than there are
+ chars in the mangled string. */
+ di->num_subs = len;
+ di->next_sub = 0;
+ di->did_subs = 0;
+
+ di->last_name = NULL;
+
+ di->expansion = 0;
+}
+
+/* Entry point for the demangler. If MANGLED is a g++ v3 ABI mangled
+ name, return a buffer allocated with malloc holding the demangled
+ name. OPTIONS is the usual libiberty demangler options. On
+ success, this sets *PALC to the allocated size of the returned
+ buffer. On failure, this sets *PALC to 0 for a bad name, or 1 for
+ a memory allocation failure. On failure, this returns NULL. */
+
+static char *
+d_demangle (mangled, options, palc)
+ const char* mangled;
+ int options;
+ size_t *palc;
+{
+ size_t len;
+ int type;
+ struct d_info di;
+ struct demangle_component *dc;
+ int estimate;
+ char *ret;
+
+ *palc = 0;
+
+ len = strlen (mangled);
+
+ if (mangled[0] == '_' && mangled[1] == 'Z')
+ type = 0;
+ else if (strncmp (mangled, "_GLOBAL_", 8) == 0
+ && (mangled[8] == '.' || mangled[8] == '_' || mangled[8] == '$')
+ && (mangled[9] == 'D' || mangled[9] == 'I')
+ && mangled[10] == '_')
+ {
+ char *r;
+
+ r = malloc (40 + len - 11);
+ if (r == NULL)
+ *palc = 1;
+ else
+ {
+ if (mangled[9] == 'I')
+ strcpy (r, "global constructors keyed to ");
+ else
+ strcpy (r, "global destructors keyed to ");
+ strcat (r, mangled + 11);
+ }
+ return r;
+ }
+ else
+ {
+ if ((options & DMGL_TYPES) == 0)
+ return NULL;
+ type = 1;
+ }
+
+ cplus_demangle_init_info (mangled, options, len, &di);
+
+ {
+#ifdef CP_DYNAMIC_ARRAYS
+ __extension__ struct demangle_component comps[di.num_comps];
+ __extension__ struct demangle_component *subs[di.num_subs];
+
+ di.comps = &comps[0];
+ di.subs = &subs[0];
+#else
+ di.comps = ((struct demangle_component *)
+ malloc (di.num_comps * sizeof (struct demangle_component)));
+ di.subs = ((struct demangle_component **)
+ malloc (di.num_subs * sizeof (struct demangle_component *)));
+ if (di.comps == NULL || di.subs == NULL)
+ {
+ if (di.comps != NULL)
+ free (di.comps);
+ if (di.subs != NULL)
+ free (di.subs);
+ *palc = 1;
+ return NULL;
+ }
+#endif
+
+ if (! type)
+ dc = cplus_demangle_mangled_name (&di, 1);
+ else
+ dc = cplus_demangle_type (&di);
+
+ /* If DMGL_PARAMS is set, then if we didn't consume the entire
+ mangled string, then we didn't successfully demangle it. If
+ DMGL_PARAMS is not set, we didn't look at the trailing
+ parameters. */
+ if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
+ dc = NULL;
+
+#ifdef CP_DEMANGLE_DEBUG
+ if (dc == NULL)
+ printf ("failed demangling\n");
+ else
+ d_dump (dc, 0);
+#endif
+
+ /* We try to guess the length of the demangled string, to minimize
+ calls to realloc during demangling. */
+ estimate = len + di.expansion + 10 * di.did_subs;
+ estimate += estimate / 8;
+
+ ret = NULL;
+ if (dc != NULL)
+ ret = cplus_demangle_print (options, dc, estimate, palc);
+
+#ifndef CP_DYNAMIC_ARRAYS
+ free (di.comps);
+ free (di.subs);
+#endif
+
+#ifdef CP_DEMANGLE_DEBUG
+ if (ret != NULL)
+ {
+ int rlen;
+
+ rlen = strlen (ret);
+ if (rlen > 2 * estimate)
+ printf ("*** Length %d much greater than estimate %d\n",
+ rlen, estimate);
+ else if (rlen > estimate)
+ printf ("*** Length %d greater than estimate %d\n",
+ rlen, estimate);
+ else if (rlen < estimate / 2)
+ printf ("*** Length %d much less than estimate %d\n",
+ rlen, estimate);
+ }
+#endif
+ }
+
+ return ret;
+}
+
+static char *
+cplus_demangle_v3 (mangled, options)
+ const char* mangled;
+ int options;
+{
+ size_t alc;
+
+ return d_demangle (mangled, options, &alc);
+}
+
+/* Demangle a Java symbol. Java uses a subset of the V3 ABI C++ mangling
+ conventions, but the output formatting is a little different.
+ This instructs the C++ demangler not to emit pointer characters ("*"), and
+ to use Java's namespace separator symbol ("." instead of "::"). It then
+ does an additional pass over the demangled output to replace instances
+ of JArray<TYPE> with TYPE[]. */
+
+static char *
+java_demangle_v3 (mangled)
+ const char* mangled;
+{
+ size_t alc;
+ char *demangled;
+ int nesting;
+ char *from;
+ char *to;
+
+ demangled = d_demangle (mangled, DMGL_JAVA | DMGL_PARAMS, &alc);
+
+ if (demangled == NULL)
+ return NULL;
+
+ nesting = 0;
+ from = demangled;
+ to = from;
+ while (*from != '\0')
+ {
+ if (strncmp (from, "JArray<", 7) == 0)
+ {
+ from += 7;
+ ++nesting;
+ }
+ else if (nesting > 0 && *from == '>')
+ {
+ while (to > demangled && to[-1] == ' ')
+ --to;
+ *to++ = '[';
+ *to++ = ']';
+ --nesting;
+ ++from;
+ }
+ else
+ *to++ = *from++;
+ }
+
+ *to = '\0';
+
+ return demangled;
+}
+
+#ifndef IN_GLIBCPP_V3
+
+/* Demangle a string in order to find out whether it is a constructor
+ or destructor. Return non-zero on success. Set *CTOR_KIND and
+ *DTOR_KIND appropriately. */
+
+static int
+is_ctor_or_dtor (mangled, ctor_kind, dtor_kind)
+ const char *mangled;
+ enum gnu_v3_ctor_kinds *ctor_kind;
+ enum gnu_v3_dtor_kinds *dtor_kind;
+{
+ struct d_info di;
+ struct demangle_component *dc;
+ int ret;
+
+ *ctor_kind = (enum gnu_v3_ctor_kinds) 0;
+ *dtor_kind = (enum gnu_v3_dtor_kinds) 0;
+
+ cplus_demangle_init_info (mangled, DMGL_GNU_V3, strlen (mangled), &di);
+
+ {
+#ifdef CP_DYNAMIC_ARRAYS
+ __extension__ struct demangle_component comps[di.num_comps];
+ __extension__ struct demangle_component *subs[di.num_subs];
+
+ di.comps = &comps[0];
+ di.subs = &subs[0];
+#else
+ di.comps = ((struct demangle_component *)
+ malloc (di.num_comps * sizeof (struct demangle_component)));
+ di.subs = ((struct demangle_component **)
+ malloc (di.num_subs * sizeof (struct demangle_component *)));
+ if (di.comps == NULL || di.subs == NULL)
+ {
+ if (di.comps != NULL)
+ free (di.comps);
+ if (di.subs != NULL)
+ free (di.subs);
+ return 0;
+ }
+#endif
+
+ dc = cplus_demangle_mangled_name (&di, 1);
+
+ /* Note that because we did not pass DMGL_PARAMS, we don't expect
+ to demangle the entire string. */
+
+ ret = 0;
+ while (dc != NULL)
+ {
+ switch (dc->type)
+ {
+ default:
+ dc = NULL;
+ break;
+ case DEMANGLE_COMPONENT_TYPED_NAME:
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ dc = d_left (dc);
+ break;
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ dc = d_right (dc);
+ break;
+ case DEMANGLE_COMPONENT_CTOR:
+ *ctor_kind = dc->u.s_ctor.kind;
+ ret = 1;
+ dc = NULL;
+ break;
+ case DEMANGLE_COMPONENT_DTOR:
+ *dtor_kind = dc->u.s_dtor.kind;
+ ret = 1;
+ dc = NULL;
+ break;
+ }
+ }
+
+#ifndef CP_DYNAMIC_ARRAYS
+ free (di.subs);
+ free (di.comps);
+#endif
+ }
+
+ return ret;
+}
+
+/* Return whether NAME is the mangled form of a g++ V3 ABI constructor
+ name. A non-zero return indicates the type of constructor. */
+
+enum gnu_v3_ctor_kinds
+is_gnu_v3_mangled_ctor (name)
+ const char *name;
+{
+ enum gnu_v3_ctor_kinds ctor_kind;
+ enum gnu_v3_dtor_kinds dtor_kind;
+
+ if (! is_ctor_or_dtor (name, &ctor_kind, &dtor_kind))
+ return (enum gnu_v3_ctor_kinds) 0;
+ return ctor_kind;
+}
+
+
+/* Return whether NAME is the mangled form of a g++ V3 ABI destructor
+ name. A non-zero return indicates the type of destructor. */
+
+enum gnu_v3_dtor_kinds
+is_gnu_v3_mangled_dtor (name)
+ const char *name;
+{
+ enum gnu_v3_ctor_kinds ctor_kind;
+ enum gnu_v3_dtor_kinds dtor_kind;
+
+ if (! is_ctor_or_dtor (name, &ctor_kind, &dtor_kind))
+ return (enum gnu_v3_dtor_kinds) 0;
+ return dtor_kind;
+}
+
+#endif /* IN_GLIBCPP_V3 */
+/* Demangler for GNU C++
+ Copyright 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.uucp)
+ Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling
+ Modified by Satish Pai (pai@apollo.hp.com) for HP demangling
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+In addition to the permissions in the GNU Library General Public
+License, the Free Software Foundation gives you unlimited permission
+to link the compiled version of this file into combinations with other
+programs, and to distribute those combinations without any restriction
+coming from the use of this file. (The Library Public License
+restrictions do apply in other respects; for example, they cover
+modification of the file, and distribution when not linked into a
+combined executable.)
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* This file exports two functions; cplus_mangle_opname and cplus_demangle.
+
+ This file imports xmalloc and g_realloc, which are like malloc and
+ realloc except that they generate a fatal error if there is no
+ available memory. */
+
+/* This file lives in both GCC and libiberty. When making changes, please
+ try not to break either. */
+
+#undef CURRENT_DEMANGLING_STYLE
+#define CURRENT_DEMANGLING_STYLE work->options
+
+static char *ada_demangle PARAMS ((const char *, int));
+
+#define min(X,Y) (((X) < (Y)) ? (X) : (Y))
+
+/* A value at least one greater than the maximum number of characters
+ that will be output when using the `%d' format with `printf'. */
+#define INTBUF_SIZE 32
+
+extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN;
+
+/* In order to allow a single demangler executable to demangle strings
+ using various common values of CPLUS_MARKER, as well as any specific
+ one set at compile time, we maintain a string containing all the
+ commonly used ones, and check to see if the marker we are looking for
+ is in that string. CPLUS_MARKER is usually '$' on systems where the
+ assembler can deal with that. Where the assembler can't, it's usually
+ '.' (but on many systems '.' is used for other things). We put the
+ current defined CPLUS_MARKER first (which defaults to '$'), followed
+ by the next most common value, followed by an explicit '$' in case
+ the value of CPLUS_MARKER is not '$'.
+
+ We could avoid this if we could just get g++ to tell us what the actual
+ cplus marker character is as part of the debug information, perhaps by
+ ensuring that it is the character that terminates the gcc<n>_compiled
+ marker symbol (FIXME). */
+
+#if !defined (CPLUS_MARKER)
+#define CPLUS_MARKER '$'
+#endif
+
+static enum demangling_styles current_demangling_style = auto_demangling;
+
+static char cplus_markers[] = { CPLUS_MARKER, '.', '$', '\0' };
+
+static char char_str[2] = { '\000', '\000' };
+
+typedef struct string /* Beware: these aren't required to be */
+{ /* '\0' terminated. */
+ char *b; /* pointer to start of string */
+ char *p; /* pointer after last character */
+ char *e; /* pointer after end of allocated space */
+} string;
+
+/* Stuff that is shared between sub-routines.
+ Using a shared structure allows cplus_demangle to be reentrant. */
+
+struct work_stuff
+{
+ int options;
+ char **typevec;
+ char **ktypevec;
+ char **btypevec;
+ int numk;
+ int numb;
+ int ksize;
+ int bsize;
+ int ntypes;
+ int typevec_size;
+ int constructor;
+ int destructor;
+ int static_type; /* A static member function */
+ int temp_start; /* index in demangled to start of template args */
+ int type_quals; /* The type qualifiers. */
+ int dllimported; /* Symbol imported from a PE DLL */
+ char **tmpl_argvec; /* Template function arguments. */
+ int ntmpl_args; /* The number of template function arguments. */
+ int forgetting_types; /* Nonzero if we are not remembering the types
+ we see. */
+ string* previous_argument; /* The last function argument demangled. */
+ int nrepeats; /* The number of times to repeat the previous
+ argument. */
+};
+
+#define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI)
+#define PRINT_ARG_TYPES (work -> options & DMGL_PARAMS)
+
+static const struct optable
+{
+ const char *const in;
+ const char *const out;
+ const int flags;
+} optable[] = {
+ {"nw", " new", DMGL_ANSI}, /* new (1.92, ansi) */
+ {"dl", " delete", DMGL_ANSI}, /* new (1.92, ansi) */
+ {"new", " new", 0}, /* old (1.91, and 1.x) */
+ {"delete", " delete", 0}, /* old (1.91, and 1.x) */
+ {"vn", " new []", DMGL_ANSI}, /* GNU, pending ansi */
+ {"vd", " delete []", DMGL_ANSI}, /* GNU, pending ansi */
+ {"as", "=", DMGL_ANSI}, /* ansi */
+ {"ne", "!=", DMGL_ANSI}, /* old, ansi */
+ {"eq", "==", DMGL_ANSI}, /* old, ansi */
+ {"ge", ">=", DMGL_ANSI}, /* old, ansi */
+ {"gt", ">", DMGL_ANSI}, /* old, ansi */
+ {"le", "<=", DMGL_ANSI}, /* old, ansi */
+ {"lt", "<", DMGL_ANSI}, /* old, ansi */
+ {"plus", "+", 0}, /* old */
+ {"pl", "+", DMGL_ANSI}, /* ansi */
+ {"apl", "+=", DMGL_ANSI}, /* ansi */
+ {"minus", "-", 0}, /* old */
+ {"mi", "-", DMGL_ANSI}, /* ansi */
+ {"ami", "-=", DMGL_ANSI}, /* ansi */
+ {"mult", "*", 0}, /* old */
+ {"ml", "*", DMGL_ANSI}, /* ansi */
+ {"amu", "*=", DMGL_ANSI}, /* ansi (ARM/Lucid) */
+ {"aml", "*=", DMGL_ANSI}, /* ansi (GNU/g++) */
+ {"convert", "+", 0}, /* old (unary +) */
+ {"negate", "-", 0}, /* old (unary -) */
+ {"trunc_mod", "%", 0}, /* old */
+ {"md", "%", DMGL_ANSI}, /* ansi */
+ {"amd", "%=", DMGL_ANSI}, /* ansi */
+ {"trunc_div", "/", 0}, /* old */
+ {"dv", "/", DMGL_ANSI}, /* ansi */
+ {"adv", "/=", DMGL_ANSI}, /* ansi */
+ {"truth_andif", "&&", 0}, /* old */
+ {"aa", "&&", DMGL_ANSI}, /* ansi */
+ {"truth_orif", "||", 0}, /* old */
+ {"oo", "||", DMGL_ANSI}, /* ansi */
+ {"truth_not", "!", 0}, /* old */
+ {"nt", "!", DMGL_ANSI}, /* ansi */
+ {"postincrement","++", 0}, /* old */
+ {"pp", "++", DMGL_ANSI}, /* ansi */
+ {"postdecrement","--", 0}, /* old */
+ {"mm", "--", DMGL_ANSI}, /* ansi */
+ {"bit_ior", "|", 0}, /* old */
+ {"or", "|", DMGL_ANSI}, /* ansi */
+ {"aor", "|=", DMGL_ANSI}, /* ansi */
+ {"bit_xor", "^", 0}, /* old */
+ {"er", "^", DMGL_ANSI}, /* ansi */
+ {"aer", "^=", DMGL_ANSI}, /* ansi */
+ {"bit_and", "&", 0}, /* old */
+ {"ad", "&", DMGL_ANSI}, /* ansi */
+ {"aad", "&=", DMGL_ANSI}, /* ansi */
+ {"bit_not", "~", 0}, /* old */
+ {"co", "~", DMGL_ANSI}, /* ansi */
+ {"call", "()", 0}, /* old */
+ {"cl", "()", DMGL_ANSI}, /* ansi */
+ {"alshift", "<<", 0}, /* old */
+ {"ls", "<<", DMGL_ANSI}, /* ansi */
+ {"als", "<<=", DMGL_ANSI}, /* ansi */
+ {"arshift", ">>", 0}, /* old */
+ {"rs", ">>", DMGL_ANSI}, /* ansi */
+ {"ars", ">>=", DMGL_ANSI}, /* ansi */
+ {"component", "->", 0}, /* old */
+ {"pt", "->", DMGL_ANSI}, /* ansi; Lucid C++ form */
+ {"rf", "->", DMGL_ANSI}, /* ansi; ARM/GNU form */
+ {"indirect", "*", 0}, /* old */
+ {"method_call", "->()", 0}, /* old */
+ {"addr", "&", 0}, /* old (unary &) */
+ {"array", "[]", 0}, /* old */
+ {"vc", "[]", DMGL_ANSI}, /* ansi */
+ {"compound", ", ", 0}, /* old */
+ {"cm", ", ", DMGL_ANSI}, /* ansi */
+ {"cond", "?:", 0}, /* old */
+ {"cn", "?:", DMGL_ANSI}, /* pseudo-ansi */
+ {"max", ">?", 0}, /* old */
+ {"mx", ">?", DMGL_ANSI}, /* pseudo-ansi */
+ {"min", "<?", 0}, /* old */
+ {"mn", "<?", DMGL_ANSI}, /* pseudo-ansi */
+ {"nop", "", 0}, /* old (for operator=) */
+ {"rm", "->*", DMGL_ANSI}, /* ansi */
+ {"sz", "sizeof ", DMGL_ANSI} /* pseudo-ansi */
+};
+
+/* These values are used to indicate the various type varieties.
+ They are all non-zero so that they can be used as `success'
+ values. */
+typedef enum type_kind_t
+{
+ tk_none,
+ tk_pointer,
+ tk_reference,
+ tk_integral,
+ tk_bool,
+ tk_char,
+ tk_real
+} type_kind_t;
+
+static const struct demangler_engine libiberty_demanglers[] =
+{
+ {
+ NO_DEMANGLING_STYLE_STRING,
+ no_demangling,
+ "Demangling disabled"
+ }
+ ,
+ {
+ AUTO_DEMANGLING_STYLE_STRING,
+ auto_demangling,
+ "Automatic selection based on executable"
+ }
+ ,
+ {
+ GNU_DEMANGLING_STYLE_STRING,
+ gnu_demangling,
+ "GNU (g++) style demangling"
+ }
+ ,
+ {
+ LUCID_DEMANGLING_STYLE_STRING,
+ lucid_demangling,
+ "Lucid (lcc) style demangling"
+ }
+ ,
+ {
+ ARM_DEMANGLING_STYLE_STRING,
+ arm_demangling,
+ "ARM style demangling"
+ }
+ ,
+ {
+ HP_DEMANGLING_STYLE_STRING,
+ hp_demangling,
+ "HP (aCC) style demangling"
+ }
+ ,
+ {
+ EDG_DEMANGLING_STYLE_STRING,
+ edg_demangling,
+ "EDG style demangling"
+ }
+ ,
+ {
+ GNU_V3_DEMANGLING_STYLE_STRING,
+ gnu_v3_demangling,
+ "GNU (g++) V3 ABI-style demangling"
+ }
+ ,
+ {
+ JAVA_DEMANGLING_STYLE_STRING,
+ java_demangling,
+ "Java style demangling"
+ }
+ ,
+ {
+ GNAT_DEMANGLING_STYLE_STRING,
+ gnat_demangling,
+ "GNAT style demangling"
+ }
+ ,
+ {
+ NULL, unknown_demangling, NULL
+ }
+};
+
+#define STRING_EMPTY(str) ((str) -> b == (str) -> p)
+#define APPEND_BLANK(str) {if (!STRING_EMPTY(str)) \
+ string_append(str, " ");}
+#define LEN_STRING(str) ( (STRING_EMPTY(str))?0:((str)->p - (str)->b))
+
+/* The scope separator appropriate for the language being demangled. */
+
+#define SCOPE_STRING(work) ((work->options & DMGL_JAVA) ? "." : "::")
+
+#define ARM_VTABLE_STRING "__vtbl__" /* Lucid/ARM virtual table prefix */
+#define ARM_VTABLE_STRLEN 8 /* strlen (ARM_VTABLE_STRING) */
+
+/* Prototypes for local functions */
+
+static void
+delete_work_stuff PARAMS ((struct work_stuff *));
+
+static void
+delete_non_B_K_work_stuff PARAMS ((struct work_stuff *));
+
+static char *
+mop_up PARAMS ((struct work_stuff *, string *, int));
+
+static void
+squangle_mop_up PARAMS ((struct work_stuff *));
+
+static void
+work_stuff_copy_to_from PARAMS ((struct work_stuff *, struct work_stuff *));
+
+#if 0
+static int
+demangle_method_args PARAMS ((struct work_stuff *, const char **, string *));
+#endif
+
+static char *
+internal_cplus_demangle PARAMS ((struct work_stuff *, const char *));
+
+static int
+demangle_template_template_parm PARAMS ((struct work_stuff *work,
+ const char **, string *));
+
+static int
+demangle_template PARAMS ((struct work_stuff *work, const char **, string *,
+ string *, int, int));
+
+static int
+arm_pt PARAMS ((struct work_stuff *, const char *, int, const char **,
+ const char **));
+
+static int
+demangle_class_name PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+demangle_qualified PARAMS ((struct work_stuff *, const char **, string *,
+ int, int));
+
+static int
+demangle_class PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+demangle_fund_type PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+demangle_signature PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+demangle_prefix PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+gnu_special PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+arm_special PARAMS ((const char **, string *));
+
+static void
+string_need PARAMS ((string *, int));
+
+static void
+string_delete PARAMS ((string *));
+
+static void
+string_init PARAMS ((string *));
+
+static void
+string_clear PARAMS ((string *));
+
+#if 0
+static int
+string_empty PARAMS ((string *));
+#endif
+
+static void
+string_append PARAMS ((string *, const char *));
+
+static void
+string_appends PARAMS ((string *, string *));
+
+static void
+string_appendn PARAMS ((string *, const char *, int));
+
+static void
+string_prepend PARAMS ((string *, const char *));
+
+static void
+string_prependn PARAMS ((string *, const char *, int));
+
+static void
+string_append_template_idx PARAMS ((string *, int));
+
+static int
+get_count PARAMS ((const char **, int *));
+
+static int
+consume_count PARAMS ((const char **));
+
+static int
+consume_count_with_underscores PARAMS ((const char**));
+
+static int
+demangle_args PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+demangle_nested_args PARAMS ((struct work_stuff*, const char**, string*));
+
+static int
+do_type PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+do_arg PARAMS ((struct work_stuff *, const char **, string *));
+
+static void
+demangle_function_name PARAMS ((struct work_stuff *, const char **, string *,
+ const char *));
+
+static int
+iterate_demangle_function PARAMS ((struct work_stuff *,
+ const char **, string *, const char *));
+
+static void
+remember_type PARAMS ((struct work_stuff *, const char *, int));
+
+static void
+remember_Btype PARAMS ((struct work_stuff *, const char *, int, int));
+
+static int
+register_Btype PARAMS ((struct work_stuff *));
+
+static void
+remember_Ktype PARAMS ((struct work_stuff *, const char *, int));
+
+static void
+forget_types PARAMS ((struct work_stuff *));
+
+static void
+forget_B_and_K_types PARAMS ((struct work_stuff *));
+
+static void
+string_prepends PARAMS ((string *, string *));
+
+static int
+demangle_template_value_parm PARAMS ((struct work_stuff*, const char**,
+ string*, type_kind_t));
+
+static int
+do_hpacc_template_const_value PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+do_hpacc_template_literal PARAMS ((struct work_stuff *, const char **, string *));
+
+static int
+snarf_numeric_literal PARAMS ((const char **, string *));
+
+/* There is a TYPE_QUAL value for each type qualifier. They can be
+ combined by bitwise-or to form the complete set of qualifiers for a
+ type. */
+
+#define TYPE_UNQUALIFIED 0x0
+#define TYPE_QUAL_CONST 0x1
+#define TYPE_QUAL_VOLATILE 0x2
+#define TYPE_QUAL_RESTRICT 0x4
+
+static int
+code_for_qualifier PARAMS ((int));
+
+static const char*
+qualifier_string PARAMS ((int));
+
+static const char*
+demangle_qualifier PARAMS ((int));
+
+static int
+demangle_expression PARAMS ((struct work_stuff *, const char **, string *,
+ type_kind_t));
+
+static int
+demangle_integral_value PARAMS ((struct work_stuff *, const char **,
+ string *));
+
+static int
+demangle_real_value PARAMS ((struct work_stuff *, const char **, string *));
+
+static void
+demangle_arm_hp_template PARAMS ((struct work_stuff *, const char **, int,
+ string *));
+
+static void
+recursively_demangle PARAMS ((struct work_stuff *, const char **, string *,
+ int));
+
+static void
+grow_vect PARAMS ((char **, size_t *, size_t, int));
+
+/* Translate count to integer, consuming tokens in the process.
+ Conversion terminates on the first non-digit character.
+
+ Trying to consume something that isn't a count results in no
+ consumption of input and a return of -1.
+
+ Overflow consumes the rest of the digits, and returns -1. */
+
+static int
+consume_count (type)
+ const char **type;
+{
+ int count = 0;
+
+ if (! g_ascii_isdigit ((unsigned char)**type))
+ return -1;
+
+ while (g_ascii_isdigit ((unsigned char)**type))
+ {
+ count *= 10;
+
+ /* Check for overflow.
+ We assume that count is represented using two's-complement;
+ no power of two is divisible by ten, so if an overflow occurs
+ when multiplying by ten, the result will not be a multiple of
+ ten. */
+ if ((count % 10) != 0)
+ {
+ while (g_ascii_isdigit ((unsigned char) **type))
+ (*type)++;
+ return -1;
+ }
+
+ count += **type - '0';
+ (*type)++;
+ }
+
+ if (count < 0)
+ count = -1;
+
+ return (count);
+}
+
+
+/* Like consume_count, but for counts that are preceded and followed
+ by '_' if they are greater than 10. Also, -1 is returned for
+ failure, since 0 can be a valid value. */
+
+static int
+consume_count_with_underscores (mangled)
+ const char **mangled;
+{
+ int idx;
+
+ if (**mangled == '_')
+ {
+ (*mangled)++;
+ if (!g_ascii_isdigit ((unsigned char)**mangled))
+ return -1;
+
+ idx = consume_count (mangled);
+ if (**mangled != '_')
+ /* The trailing underscore was missing. */
+ return -1;
+
+ (*mangled)++;
+ }
+ else
+ {
+ if (**mangled < '0' || **mangled > '9')
+ return -1;
+
+ idx = **mangled - '0';
+ (*mangled)++;
+ }
+
+ return idx;
+}
+
+/* C is the code for a type-qualifier. Return the TYPE_QUAL
+ corresponding to this qualifier. */
+
+static int
+code_for_qualifier (c)
+ int c;
+{
+ switch (c)
+ {
+ case 'C':
+ return TYPE_QUAL_CONST;
+
+ case 'V':
+ return TYPE_QUAL_VOLATILE;
+
+ case 'u':
+ return TYPE_QUAL_RESTRICT;
+
+ default:
+ break;
+ }
+
+ /* C was an invalid qualifier. */
+ abort ();
+}
+
+/* Return the string corresponding to the qualifiers given by
+ TYPE_QUALS. */
+
+static const char*
+qualifier_string (type_quals)
+ int type_quals;
+{
+ switch (type_quals)
+ {
+ case TYPE_UNQUALIFIED:
+ return "";
+
+ case TYPE_QUAL_CONST:
+ return "const";
+
+ case TYPE_QUAL_VOLATILE:
+ return "volatile";
+
+ case TYPE_QUAL_RESTRICT:
+ return "__restrict";
+
+ case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE:
+ return "const volatile";
+
+ case TYPE_QUAL_CONST | TYPE_QUAL_RESTRICT:
+ return "const __restrict";
+
+ case TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT:
+ return "volatile __restrict";
+
+ case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT:
+ return "const volatile __restrict";
+
+ default:
+ break;
+ }
+
+ /* TYPE_QUALS was an invalid qualifier set. */
+ abort ();
+}
+
+/* C is the code for a type-qualifier. Return the string
+ corresponding to this qualifier. This function should only be
+ called with a valid qualifier code. */
+
+static const char*
+demangle_qualifier (c)
+ int c;
+{
+ return qualifier_string (code_for_qualifier (c));
+}
+
+/* Takes operator name as e.g. "++" and returns mangled
+ operator name (e.g. "postincrement_expr"), or NULL if not found.
+
+ If OPTIONS & DMGL_ANSI == 1, return the ANSI name;
+ if OPTIONS & DMGL_ANSI == 0, return the old GNU name. */
+
+/* Add a routine to set the demangling style to be sure it is valid and
+ allow for any demangler initialization that maybe necessary. */
+
+/* Do string name to style translation */
+
+/* char *cplus_demangle (const char *mangled, int options)
+
+ If MANGLED is a mangled function name produced by GNU C++, then
+ a pointer to a @code{malloc}ed string giving a C++ representation
+ of the name will be returned; otherwise NULL will be returned.
+ It is the caller's responsibility to free the string which
+ is returned.
+
+ The OPTIONS arg may contain one or more of the following bits:
+
+ DMGL_ANSI ANSI qualifiers such as `const' and `void' are
+ included.
+ DMGL_PARAMS Function parameters are included.
+
+ For example,
+
+ cplus_demangle ("foo__1Ai", DMGL_PARAMS) => "A::foo(int)"
+ cplus_demangle ("foo__1Ai", DMGL_PARAMS | DMGL_ANSI) => "A::foo(int)"
+ cplus_demangle ("foo__1Ai", 0) => "A::foo"
+
+ cplus_demangle ("foo__1Afe", DMGL_PARAMS) => "A::foo(float,...)"
+ cplus_demangle ("foo__1Afe", DMGL_PARAMS | DMGL_ANSI)=> "A::foo(float,...)"
+ cplus_demangle ("foo__1Afe", 0) => "A::foo"
+
+ Note that any leading underscores, or other such characters prepended by
+ the compilation system, are presumed to have already been stripped from
+ MANGLED. */
+
+char *
+sysprof_cplus_demangle (mangled, options)
+ const char *mangled;
+ int options;
+{
+ char *ret;
+ struct work_stuff work[1];
+
+ if (current_demangling_style == no_demangling)
+ return g_strdup (mangled);
+
+ memset ((char *) work, 0, sizeof (work));
+ work->options = options;
+ if ((work->options & DMGL_STYLE_MASK) == 0)
+ work->options |= (int) current_demangling_style & DMGL_STYLE_MASK;
+
+ /* The V3 ABI demangling is implemented elsewhere. */
+ if (GNU_V3_DEMANGLING || AUTO_DEMANGLING)
+ {
+ ret = cplus_demangle_v3 (mangled, work->options);
+ if (ret || GNU_V3_DEMANGLING)
+ return ret;
+ }
+
+ if (JAVA_DEMANGLING)
+ {
+ ret = java_demangle_v3 (mangled);
+ if (ret)
+ return ret;
+ }
+
+ if (GNAT_DEMANGLING)
+ return ada_demangle(mangled,options);
+
+ ret = internal_cplus_demangle (work, mangled);
+ squangle_mop_up (work);
+ return (ret);
+}
+
+
+/* Assuming *OLD_VECT points to an array of *SIZE objects of size
+ ELEMENT_SIZE, grow it to contain at least MIN_SIZE objects,
+ updating *OLD_VECT and *SIZE as necessary. */
+
+static void
+grow_vect (old_vect, size, min_size, element_size)
+ char **old_vect;
+ size_t *size;
+ size_t min_size;
+ int element_size;
+{
+ if (*size < min_size)
+ {
+ *size *= 2;
+ if (*size < min_size)
+ *size = min_size;
+ *old_vect = (void *) g_realloc (*old_vect, *size * element_size);
+ }
+}
+
+/* Demangle ada names:
+ 1. Discard final __{DIGIT}+ or ${DIGIT}+
+ 2. Convert other instances of embedded "__" to `.'.
+ 3. Discard leading _ada_.
+ 4. Remove everything after first ___ if it is followed by 'X'.
+ 5. Put symbols that should be suppressed in <...> brackets.
+ The resulting string is valid until the next call of ada_demangle. */
+
+static char *
+ada_demangle (mangled, option)
+ const char *mangled;
+ int option ATTRIBUTE_UNUSED;
+{
+ int i, j;
+ int len0;
+ const char* p;
+ char *demangled = NULL;
+ int changed;
+ size_t demangled_size = 0;
+
+ changed = 0;
+
+ if (strncmp (mangled, "_ada_", 5) == 0)
+ {
+ mangled += 5;
+ changed = 1;
+ }
+
+ if (mangled[0] == '_' || mangled[0] == '<')
+ goto Suppress;
+
+ p = strstr (mangled, "___");
+ if (p == NULL)
+ len0 = strlen (mangled);
+ else
+ {
+ if (p[3] == 'X')
+ {
+ len0 = p - mangled;
+ changed = 1;
+ }
+ else
+ goto Suppress;
+ }
+
+ /* Make demangled big enough for possible expansion by operator name. */
+ grow_vect (&demangled,
+ &demangled_size, 2 * len0 + 1,
+ sizeof (char));
+
+ if (g_ascii_isdigit ((unsigned char) mangled[len0 - 1])) {
+ for (i = len0 - 2; i >= 0 && g_ascii_isdigit ((unsigned char) mangled[i]); i -= 1)
+ ;
+ if (i > 1 && mangled[i] == '_' && mangled[i - 1] == '_')
+ {
+ len0 = i - 1;
+ changed = 1;
+ }
+ else if (mangled[i] == '$')
+ {
+ len0 = i;
+ changed = 1;
+ }
+ }
+
+ for (i = 0, j = 0; i < len0 && ! g_ascii_isalpha ((unsigned char)mangled[i]);
+ i += 1, j += 1)
+ demangled[j] = mangled[i];
+
+ while (i < len0)
+ {
+ if (i < len0 - 2 && mangled[i] == '_' && mangled[i + 1] == '_')
+ {
+ demangled[j] = '.';
+ changed = 1;
+ i += 2; j += 1;
+ }
+ else
+ {
+ demangled[j] = mangled[i];
+ i += 1; j += 1;
+ }
+ }
+ demangled[j] = '\000';
+
+ for (i = 0; demangled[i] != '\0'; i += 1)
+ if (g_ascii_isupper ((unsigned char)demangled[i]) || demangled[i] == ' ')
+ goto Suppress;
+
+ if (! changed)
+ return NULL;
+ else
+ return demangled;
+
+ Suppress:
+ grow_vect (&demangled,
+ &demangled_size, strlen (mangled) + 3,
+ sizeof (char));
+
+ if (mangled[0] == '<')
+ strcpy (demangled, mangled);
+ else
+ sprintf (demangled, "<%s>", mangled);
+
+ return demangled;
+}
+
+/* This function performs most of what cplus_demangle use to do, but
+ to be able to demangle a name with a B, K or n code, we need to
+ have a longer term memory of what types have been seen. The original
+ now initializes and cleans up the squangle code info, while internal
+ calls go directly to this routine to avoid resetting that info. */
+
+static char *
+internal_cplus_demangle (work, mangled)
+ struct work_stuff *work;
+ const char *mangled;
+{
+
+ string decl;
+ int success = 0;
+ char *demangled = NULL;
+ int s1, s2, s3, s4;
+ s1 = work->constructor;
+ s2 = work->destructor;
+ s3 = work->static_type;
+ s4 = work->type_quals;
+ work->constructor = work->destructor = 0;
+ work->type_quals = TYPE_UNQUALIFIED;
+ work->dllimported = 0;
+
+ if ((mangled != NULL) && (*mangled != '\0'))
+ {
+ string_init (&decl);
+
+ /* First check to see if gnu style demangling is active and if the
+ string to be demangled contains a CPLUS_MARKER. If so, attempt to
+ recognize one of the gnu special forms rather than looking for a
+ standard prefix. In particular, don't worry about whether there
+ is a "__" string in the mangled string. Consider "_$_5__foo" for
+ example. */
+
+ if ((AUTO_DEMANGLING || GNU_DEMANGLING))
+ {
+ success = gnu_special (work, &mangled, &decl);
+ }
+ if (!success)
+ {
+ success = demangle_prefix (work, &mangled, &decl);
+ }
+ if (success && (*mangled != '\0'))
+ {
+ success = demangle_signature (work, &mangled, &decl);
+ }
+ if (work->constructor == 2)
+ {
+ string_prepend (&decl, "global constructors keyed to ");
+ work->constructor = 0;
+ }
+ else if (work->destructor == 2)
+ {
+ string_prepend (&decl, "global destructors keyed to ");
+ work->destructor = 0;
+ }
+ else if (work->dllimported == 1)
+ {
+ string_prepend (&decl, "import stub for ");
+ work->dllimported = 0;
+ }
+ demangled = mop_up (work, &decl, success);
+ }
+ work->constructor = s1;
+ work->destructor = s2;
+ work->static_type = s3;
+ work->type_quals = s4;
+ return demangled;
+}
+
+
+/* Clear out and squangling related storage */
+static void
+squangle_mop_up (work)
+ struct work_stuff *work;
+{
+ /* clean up the B and K type mangling types. */
+ forget_B_and_K_types (work);
+ if (work -> btypevec != NULL)
+ {
+ g_free ((char *) work -> btypevec);
+ }
+ if (work -> ktypevec != NULL)
+ {
+ g_free ((char *) work -> ktypevec);
+ }
+}
+
+
+/* Copy the work state and storage. */
+
+static void
+work_stuff_copy_to_from (to, from)
+ struct work_stuff *to;
+ struct work_stuff *from;
+{
+ int i;
+
+ delete_work_stuff (to);
+
+ /* Shallow-copy scalars. */
+ memcpy (to, from, sizeof (*to));
+
+ /* Deep-copy dynamic storage. */
+ if (from->typevec_size)
+ to->typevec
+ = (char **) g_malloc (from->typevec_size * sizeof (to->typevec[0]));
+
+ for (i = 0; i < from->ntypes; i++)
+ {
+ int len = strlen (from->typevec[i]) + 1;
+
+ to->typevec[i] = g_malloc (len);
+ memcpy (to->typevec[i], from->typevec[i], len);
+ }
+
+ if (from->ksize)
+ to->ktypevec
+ = (char **) g_malloc (from->ksize * sizeof (to->ktypevec[0]));
+
+ for (i = 0; i < from->numk; i++)
+ {
+ int len = strlen (from->ktypevec[i]) + 1;
+
+ to->ktypevec[i] = g_malloc (len);
+ memcpy (to->ktypevec[i], from->ktypevec[i], len);
+ }
+
+ if (from->bsize)
+ to->btypevec
+ = (char **) g_malloc (from->bsize * sizeof (to->btypevec[0]));
+
+ for (i = 0; i < from->numb; i++)
+ {
+ int len = strlen (from->btypevec[i]) + 1;
+
+ to->btypevec[i] = g_malloc (len);
+ memcpy (to->btypevec[i], from->btypevec[i], len);
+ }
+
+ if (from->ntmpl_args)
+ to->tmpl_argvec
+ = (char **) g_malloc (from->ntmpl_args * sizeof (to->tmpl_argvec[0]));
+
+ for (i = 0; i < from->ntmpl_args; i++)
+ {
+ int len = strlen (from->tmpl_argvec[i]) + 1;
+
+ to->tmpl_argvec[i] = g_malloc (len);
+ memcpy (to->tmpl_argvec[i], from->tmpl_argvec[i], len);
+ }
+
+ if (from->previous_argument)
+ {
+ to->previous_argument = (string*) g_malloc (sizeof (string));
+ string_init (to->previous_argument);
+ string_appends (to->previous_argument, from->previous_argument);
+ }
+}
+
+
+/* Delete dynamic stuff in work_stuff that is not to be re-used. */
+
+static void
+delete_non_B_K_work_stuff (work)
+ struct work_stuff *work;
+{
+ /* Discard the remembered types, if any. */
+
+ forget_types (work);
+ if (work -> typevec != NULL)
+ {
+ g_free ((char *) work -> typevec);
+ work -> typevec = NULL;
+ work -> typevec_size = 0;
+ }
+ if (work->tmpl_argvec)
+ {
+ int i;
+
+ for (i = 0; i < work->ntmpl_args; i++)
+ if (work->tmpl_argvec[i])
+ g_free ((char*) work->tmpl_argvec[i]);
+
+ g_free ((char*) work->tmpl_argvec);
+ work->tmpl_argvec = NULL;
+ }
+ if (work->previous_argument)
+ {
+ string_delete (work->previous_argument);
+ g_free ((char*) work->previous_argument);
+ work->previous_argument = NULL;
+ }
+}
+
+
+/* Delete all dynamic storage in work_stuff. */
+static void
+delete_work_stuff (work)
+ struct work_stuff *work;
+{
+ delete_non_B_K_work_stuff (work);
+ squangle_mop_up (work);
+}
+
+
+/* Clear out any mangled storage */
+
+static char *
+mop_up (work, declp, success)
+ struct work_stuff *work;
+ string *declp;
+ int success;
+{
+ char *demangled = NULL;
+
+ delete_non_B_K_work_stuff (work);
+
+ /* If demangling was successful, ensure that the demangled string is null
+ terminated and return it. Otherwise, free the demangling decl. */
+
+ if (!success)
+ {
+ string_delete (declp);
+ }
+ else
+ {
+ string_appendn (declp, "", 1);
+ demangled = declp->b;
+ }
+ return (demangled);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_signature -- demangle the signature part of a mangled name
+
+SYNOPSIS
+
+ static int
+ demangle_signature (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+DESCRIPTION
+
+ Consume and demangle the signature portion of the mangled name.
+
+ DECLP is the string where demangled output is being built. At
+ entry it contains the demangled root name from the mangled name
+ prefix. I.E. either a demangled operator name or the root function
+ name. In some special cases, it may contain nothing.
+
+ *MANGLED points to the current unconsumed location in the mangled
+ name. As tokens are consumed and demangling is performed, the
+ pointer is updated to continuously point at the next token to
+ be consumed.
+
+ Demangling GNU style mangled names is nasty because there is no
+ explicit token that marks the start of the outermost function
+ argument list. */
+
+static int
+demangle_signature (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ int success = 1;
+ int func_done = 0;
+ int expect_func = 0;
+ int expect_return_type = 0;
+ const char *oldmangled = NULL;
+ string trawname;
+ string tname;
+
+ while (success && (**mangled != '\0'))
+ {
+ switch (**mangled)
+ {
+ case 'Q':
+ oldmangled = *mangled;
+ success = demangle_qualified (work, mangled, declp, 1, 0);
+ if (success)
+ remember_type (work, oldmangled, *mangled - oldmangled);
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ expect_func = 1;
+ oldmangled = NULL;
+ break;
+
+ case 'K':
+ oldmangled = *mangled;
+ success = demangle_qualified (work, mangled, declp, 1, 0);
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ expect_func = 1;
+ }
+ oldmangled = NULL;
+ break;
+
+ case 'S':
+ /* Static member function */
+ if (oldmangled == NULL)
+ {
+ oldmangled = *mangled;
+ }
+ (*mangled)++;
+ work -> static_type = 1;
+ break;
+
+ case 'C':
+ case 'V':
+ case 'u':
+ work->type_quals |= code_for_qualifier (**mangled);
+
+ /* a qualified member function */
+ if (oldmangled == NULL)
+ oldmangled = *mangled;
+ (*mangled)++;
+ break;
+
+ case 'L':
+ /* Local class name follows after "Lnnn_" */
+ if (HP_DEMANGLING)
+ {
+ while (**mangled && (**mangled != '_'))
+ (*mangled)++;
+ if (!**mangled)
+ success = 0;
+ else
+ (*mangled)++;
+ }
+ else
+ success = 0;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (oldmangled == NULL)
+ {
+ oldmangled = *mangled;
+ }
+ work->temp_start = -1; /* uppermost call to demangle_class */
+ success = demangle_class (work, mangled, declp);
+ if (success)
+ {
+ remember_type (work, oldmangled, *mangled - oldmangled);
+ }
+ if (AUTO_DEMANGLING || GNU_DEMANGLING || EDG_DEMANGLING)
+ {
+ /* EDG and others will have the "F", so we let the loop cycle
+ if we are looking at one. */
+ if (**mangled != 'F')
+ expect_func = 1;
+ }
+ oldmangled = NULL;
+ break;
+
+ case 'B':
+ {
+ string s;
+ success = do_type (work, mangled, &s);
+ if (success)
+ {
+ string_append (&s, SCOPE_STRING (work));
+ string_prepends (declp, &s);
+ string_delete (&s);
+ }
+ oldmangled = NULL;
+ expect_func = 1;
+ }
+ break;
+
+ case 'F':
+ /* Function */
+ /* ARM/HP style demangling includes a specific 'F' character after
+ the class name. For GNU style, it is just implied. So we can
+ safely just consume any 'F' at this point and be compatible
+ with either style. */
+
+ oldmangled = NULL;
+ func_done = 1;
+ (*mangled)++;
+
+ /* For lucid/ARM/HP style we have to forget any types we might
+ have remembered up to this point, since they were not argument
+ types. GNU style considers all types seen as available for
+ back references. See comment in demangle_args() */
+
+ if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
+ {
+ forget_types (work);
+ }
+ success = demangle_args (work, mangled, declp);
+ /* After picking off the function args, we expect to either
+ find the function return type (preceded by an '_') or the
+ end of the string. */
+ if (success && (AUTO_DEMANGLING || EDG_DEMANGLING) && **mangled == '_')
+ {
+ ++(*mangled);
+ /* At this level, we do not care about the return type. */
+ success = do_type (work, mangled, &tname);
+ string_delete (&tname);
+ }
+
+ break;
+
+ case 't':
+ /* G++ Template */
+ string_init(&trawname);
+ string_init(&tname);
+ if (oldmangled == NULL)
+ {
+ oldmangled = *mangled;
+ }
+ success = demangle_template (work, mangled, &tname,
+ &trawname, 1, 1);
+ if (success)
+ {
+ remember_type (work, oldmangled, *mangled - oldmangled);
+ }
+ string_append (&tname, SCOPE_STRING (work));
+
+ string_prepends(declp, &tname);
+ if (work -> destructor & 1)
+ {
+ string_prepend (&trawname, "~");
+ string_appends (declp, &trawname);
+ work->destructor -= 1;
+ }
+ if ((work->constructor & 1) || (work->destructor & 1))
+ {
+ string_appends (declp, &trawname);
+ work->constructor -= 1;
+ }
+ string_delete(&trawname);
+ string_delete(&tname);
+ oldmangled = NULL;
+ expect_func = 1;
+ break;
+
+ case '_':
+ if ((AUTO_DEMANGLING || GNU_DEMANGLING) && expect_return_type)
+ {
+ /* Read the return type. */
+ string return_type;
+
+ (*mangled)++;
+ success = do_type (work, mangled, &return_type);
+ APPEND_BLANK (&return_type);
+
+ string_prepends (declp, &return_type);
+ string_delete (&return_type);
+ break;
+ }
+ else
+ /* At the outermost level, we cannot have a return type specified,
+ so if we run into another '_' at this point we are dealing with
+ a mangled name that is either bogus, or has been mangled by
+ some algorithm we don't know how to deal with. So just
+ reject the entire demangling. */
+ /* However, "_nnn" is an expected suffix for alternate entry point
+ numbered nnn for a function, with HP aCC, so skip over that
+ without reporting failure. pai/1997-09-04 */
+ if (HP_DEMANGLING)
+ {
+ (*mangled)++;
+ while (**mangled && g_ascii_isdigit ((unsigned char)**mangled))
+ (*mangled)++;
+ }
+ else
+ success = 0;
+ break;
+
+ case 'H':
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ /* A G++ template function. Read the template arguments. */
+ success = demangle_template (work, mangled, declp, 0, 0,
+ 0);
+ if (!(work->constructor & 1))
+ expect_return_type = 1;
+ (*mangled)++;
+ break;
+ }
+ else
+ /* fall through */
+ {;}
+
+ default:
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ /* Assume we have stumbled onto the first outermost function
+ argument token, and start processing args. */
+ func_done = 1;
+ success = demangle_args (work, mangled, declp);
+ }
+ else
+ {
+ /* Non-GNU demanglers use a specific token to mark the start
+ of the outermost function argument tokens. Typically 'F',
+ for ARM/HP-demangling, for example. So if we find something
+ we are not prepared for, it must be an error. */
+ success = 0;
+ }
+ break;
+ }
+ /*
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ */
+ {
+ if (success && expect_func)
+ {
+ func_done = 1;
+ if (LUCID_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING)
+ {
+ forget_types (work);
+ }
+ success = demangle_args (work, mangled, declp);
+ /* Since template include the mangling of their return types,
+ we must set expect_func to 0 so that we don't try do
+ demangle more arguments the next time we get here. */
+ expect_func = 0;
+ }
+ }
+ }
+ if (success && !func_done)
+ {
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
+ {
+ /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and
+ bar__3fooi is 'foo::bar(int)'. We get here when we find the
+ first case, and need to ensure that the '(void)' gets added to
+ the current declp. Note that with ARM/HP, the first case
+ represents the name of a static data member 'foo::bar',
+ which is in the current declp, so we leave it alone. */
+ success = demangle_args (work, mangled, declp);
+ }
+ }
+ if (success && PRINT_ARG_TYPES)
+ {
+ if (work->static_type)
+ string_append (declp, " static");
+ if (work->type_quals != TYPE_UNQUALIFIED)
+ {
+ APPEND_BLANK (declp);
+ string_append (declp, qualifier_string (work->type_quals));
+ }
+ }
+
+ return (success);
+}
+
+#if 0
+
+static int
+demangle_method_args (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ int success = 0;
+
+ if (work -> static_type)
+ {
+ string_append (declp, *mangled + 1);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ else
+ {
+ success = demangle_args (work, mangled, declp);
+ }
+ return (success);
+}
+
+#endif
+
+static int
+demangle_template_template_parm (work, mangled, tname)
+ struct work_stuff *work;
+ const char **mangled;
+ string *tname;
+{
+ int i;
+ int r;
+ int need_comma = 0;
+ int success = 1;
+ string temp;
+
+ string_append (tname, "template <");
+ /* get size of template parameter list */
+ if (get_count (mangled, &r))
+ {
+ for (i = 0; i < r; i++)
+ {
+ if (need_comma)
+ {
+ string_append (tname, ", ");
+ }
+
+ /* Z for type parameters */
+ if (**mangled == 'Z')
+ {
+ (*mangled)++;
+ string_append (tname, "class");
+ }
+ /* z for template parameters */
+ else if (**mangled == 'z')
+ {
+ (*mangled)++;
+ success =
+ demangle_template_template_parm (work, mangled, tname);
+ if (!success)
+ {
+ break;
+ }
+ }
+ else
+ {
+ /* temp is initialized in do_type */
+ success = do_type (work, mangled, &temp);
+ if (success)
+ {
+ string_appends (tname, &temp);
+ }
+ string_delete(&temp);
+ if (!success)
+ {
+ break;
+ }
+ }
+ need_comma = 1;
+ }
+
+ }
+ if (tname->p[-1] == '>')
+ string_append (tname, " ");
+ string_append (tname, "> class");
+ return (success);
+}
+
+static int
+demangle_expression (work, mangled, s, tk)
+ struct work_stuff *work;
+ const char** mangled;
+ string* s;
+ type_kind_t tk;
+{
+ int need_operator = 0;
+ int success;
+
+ success = 1;
+ string_appendn (s, "(", 1);
+ (*mangled)++;
+ while (success && **mangled != 'W' && **mangled != '\0')
+ {
+ if (need_operator)
+ {
+ size_t i;
+ size_t len;
+
+ success = 0;
+
+ len = strlen (*mangled);
+
+ for (i = 0; i < G_N_ELEMENTS (optable); ++i)
+ {
+ size_t l = strlen (optable[i].in);
+
+ if (l <= len
+ && memcmp (optable[i].in, *mangled, l) == 0)
+ {
+ string_appendn (s, " ", 1);
+ string_append (s, optable[i].out);
+ string_appendn (s, " ", 1);
+ success = 1;
+ (*mangled) += l;
+ break;
+ }
+ }
+
+ if (!success)
+ break;
+ }
+ else
+ need_operator = 1;
+
+ success = demangle_template_value_parm (work, mangled, s, tk);
+ }
+
+ if (**mangled != 'W')
+ success = 0;
+ else
+ {
+ string_appendn (s, ")", 1);
+ (*mangled)++;
+ }
+
+ return success;
+}
+
+static int
+demangle_integral_value (work, mangled, s)
+ struct work_stuff *work;
+ const char** mangled;
+ string* s;
+{
+ int success;
+
+ if (**mangled == 'E')
+ success = demangle_expression (work, mangled, s, tk_integral);
+ else if (**mangled == 'Q' || **mangled == 'K')
+ success = demangle_qualified (work, mangled, s, 0, 1);
+ else
+ {
+ int value;
+
+ /* By default, we let the number decide whether we shall consume an
+ underscore. */
+ int multidigit_without_leading_underscore = 0;
+ int leave_following_underscore = 0;
+
+ success = 0;
+
+ if (**mangled == '_')
+ {
+ if (mangled[0][1] == 'm')
+ {
+ /* Since consume_count_with_underscores does not handle the
+ `m'-prefix we must do it here, using consume_count and
+ adjusting underscores: we have to consume the underscore
+ matching the prepended one. */
+ multidigit_without_leading_underscore = 1;
+ string_appendn (s, "-", 1);
+ (*mangled) += 2;
+ }
+ else
+ {
+ /* Do not consume a following underscore;
+ consume_count_with_underscores will consume what
+ should be consumed. */
+ leave_following_underscore = 1;
+ }
+ }
+ else
+ {
+ /* Negative numbers are indicated with a leading `m'. */
+ if (**mangled == 'm')
+ {
+ string_appendn (s, "-", 1);
+ (*mangled)++;
+ }
+ /* Since consume_count_with_underscores does not handle
+ multi-digit numbers that do not start with an underscore,
+ and this number can be an integer template parameter,
+ we have to call consume_count. */
+ multidigit_without_leading_underscore = 1;
+ /* These multi-digit numbers never end on an underscore,
+ so if there is one then don't eat it. */
+ leave_following_underscore = 1;
+ }
+
+ /* We must call consume_count if we expect to remove a trailing
+ underscore, since consume_count_with_underscores expects
+ the leading underscore (that we consumed) if it is to handle
+ multi-digit numbers. */
+ if (multidigit_without_leading_underscore)
+ value = consume_count (mangled);
+ else
+ value = consume_count_with_underscores (mangled);
+
+ if (value != -1)
+ {
+ char buf[INTBUF_SIZE];
+ sprintf (buf, "%d", value);
+ string_append (s, buf);
+
+ /* Numbers not otherwise delimited, might have an underscore
+ appended as a delimeter, which we should skip.
+
+ ??? This used to always remove a following underscore, which
+ is wrong. If other (arbitrary) cases are followed by an
+ underscore, we need to do something more radical. */
+
+ if ((value > 9 || multidigit_without_leading_underscore)
+ && ! leave_following_underscore
+ && **mangled == '_')
+ (*mangled)++;
+
+ /* All is well. */
+ success = 1;
+ }
+ }
+
+ return success;
+}
+
+/* Demangle the real value in MANGLED. */
+
+static int
+demangle_real_value (work, mangled, s)
+ struct work_stuff *work;
+ const char **mangled;
+ string* s;
+{
+ if (**mangled == 'E')
+ return demangle_expression (work, mangled, s, tk_real);
+
+ if (**mangled == 'm')
+ {
+ string_appendn (s, "-", 1);
+ (*mangled)++;
+ }
+ while (g_ascii_isdigit ((unsigned char)**mangled))
+ {
+ string_appendn (s, *mangled, 1);
+ (*mangled)++;
+ }
+ if (**mangled == '.') /* fraction */
+ {
+ string_appendn (s, ".", 1);
+ (*mangled)++;
+ while (g_ascii_isdigit ((unsigned char)**mangled))
+ {
+ string_appendn (s, *mangled, 1);
+ (*mangled)++;
+ }
+ }
+ if (**mangled == 'e') /* exponent */
+ {
+ string_appendn (s, "e", 1);
+ (*mangled)++;
+ while (g_ascii_isdigit ((unsigned char)**mangled))
+ {
+ string_appendn (s, *mangled, 1);
+ (*mangled)++;
+ }
+ }
+
+ return 1;
+}
+
+static int
+demangle_template_value_parm (work, mangled, s, tk)
+ struct work_stuff *work;
+ const char **mangled;
+ string* s;
+ type_kind_t tk;
+{
+ int success = 1;
+
+ if (**mangled == 'Y')
+ {
+ /* The next argument is a template parameter. */
+ int idx;
+
+ (*mangled)++;
+ idx = consume_count_with_underscores (mangled);
+ if (idx == -1
+ || (work->tmpl_argvec && idx >= work->ntmpl_args)
+ || consume_count_with_underscores (mangled) == -1)
+ return -1;
+ if (work->tmpl_argvec)
+ string_append (s, work->tmpl_argvec[idx]);
+ else
+ string_append_template_idx (s, idx);
+ }
+ else if (tk == tk_integral)
+ success = demangle_integral_value (work, mangled, s);
+ else if (tk == tk_char)
+ {
+ char tmp[2];
+ int val;
+ if (**mangled == 'm')
+ {
+ string_appendn (s, "-", 1);
+ (*mangled)++;
+ }
+ string_appendn (s, "'", 1);
+ val = consume_count(mangled);
+ if (val <= 0)
+ success = 0;
+ else
+ {
+ tmp[0] = (char)val;
+ tmp[1] = '\0';
+ string_appendn (s, &tmp[0], 1);
+ string_appendn (s, "'", 1);
+ }
+ }
+ else if (tk == tk_bool)
+ {
+ int val = consume_count (mangled);
+ if (val == 0)
+ string_appendn (s, "false", 5);
+ else if (val == 1)
+ string_appendn (s, "true", 4);
+ else
+ success = 0;
+ }
+ else if (tk == tk_real)
+ success = demangle_real_value (work, mangled, s);
+ else if (tk == tk_pointer || tk == tk_reference)
+ {
+ if (**mangled == 'Q')
+ success = demangle_qualified (work, mangled, s,
+ /*isfuncname=*/0,
+ /*append=*/1);
+ else
+ {
+ int symbol_len = consume_count (mangled);
+ if (symbol_len == -1)
+ return -1;
+ if (symbol_len == 0)
+ string_appendn (s, "0", 1);
+ else
+ {
+ char *p = g_malloc (symbol_len + 1), *q;
+ strncpy (p, *mangled, symbol_len);
+ p [symbol_len] = '\0';
+ /* We use cplus_demangle here, rather than
+ internal_cplus_demangle, because the name of the entity
+ mangled here does not make use of any of the squangling
+ or type-code information we have built up thus far; it is
+ mangled independently. */
+ q = sysprof_cplus_demangle (p, work->options);
+ if (tk == tk_pointer)
+ string_appendn (s, "&", 1);
+ /* FIXME: Pointer-to-member constants should get a
+ qualifying class name here. */
+ if (q)
+ {
+ string_append (s, q);
+ g_free (q);
+ }
+ else
+ string_append (s, p);
+ g_free (p);
+ }
+ *mangled += symbol_len;
+ }
+ }
+
+ return success;
+}
+
+/* Demangle the template name in MANGLED. The full name of the
+ template (e.g., S<int>) is placed in TNAME. The name without the
+ template parameters (e.g. S) is placed in TRAWNAME if TRAWNAME is
+ non-NULL. If IS_TYPE is nonzero, this template is a type template,
+ not a function template. If both IS_TYPE and REMEMBER are nonzero,
+ the template is remembered in the list of back-referenceable
+ types. */
+
+static int
+demangle_template (work, mangled, tname, trawname, is_type, remember)
+ struct work_stuff *work;
+ const char **mangled;
+ string *tname;
+ string *trawname;
+ int is_type;
+ int remember;
+{
+ int i;
+ int r;
+ int need_comma = 0;
+ int success = 0;
+ int is_java_array = 0;
+ string temp;
+
+ (*mangled)++;
+ if (is_type)
+ {
+ /* get template name */
+ if (**mangled == 'z')
+ {
+ int idx;
+ (*mangled)++;
+ (*mangled)++;
+
+ idx = consume_count_with_underscores (mangled);
+ if (idx == -1
+ || (work->tmpl_argvec && idx >= work->ntmpl_args)
+ || consume_count_with_underscores (mangled) == -1)
+ return (0);
+
+ if (work->tmpl_argvec)
+ {
+ string_append (tname, work->tmpl_argvec[idx]);
+ if (trawname)
+ string_append (trawname, work->tmpl_argvec[idx]);
+ }
+ else
+ {
+ string_append_template_idx (tname, idx);
+ if (trawname)
+ string_append_template_idx (trawname, idx);
+ }
+ }
+ else
+ {
+ if ((r = consume_count (mangled)) <= 0
+ || (int) strlen (*mangled) < r)
+ {
+ return (0);
+ }
+ is_java_array = (work -> options & DMGL_JAVA)
+ && strncmp (*mangled, "JArray1Z", 8) == 0;
+ if (! is_java_array)
+ {
+ string_appendn (tname, *mangled, r);
+ }
+ if (trawname)
+ string_appendn (trawname, *mangled, r);
+ *mangled += r;
+ }
+ }
+ if (!is_java_array)
+ string_append (tname, "<");
+ /* get size of template parameter list */
+ if (!get_count (mangled, &r))
+ {
+ return (0);
+ }
+ if (!is_type)
+ {
+ /* Create an array for saving the template argument values. */
+ work->tmpl_argvec = (char**) g_malloc (r * sizeof (char *));
+ work->ntmpl_args = r;
+ for (i = 0; i < r; i++)
+ work->tmpl_argvec[i] = 0;
+ }
+ for (i = 0; i < r; i++)
+ {
+ if (need_comma)
+ {
+ string_append (tname, ", ");
+ }
+ /* Z for type parameters */
+ if (**mangled == 'Z')
+ {
+ (*mangled)++;
+ /* temp is initialized in do_type */
+ success = do_type (work, mangled, &temp);
+ if (success)
+ {
+ string_appends (tname, &temp);
+
+ if (!is_type)
+ {
+ /* Save the template argument. */
+ int len = temp.p - temp.b;
+ work->tmpl_argvec[i] = g_malloc (len + 1);
+ memcpy (work->tmpl_argvec[i], temp.b, len);
+ work->tmpl_argvec[i][len] = '\0';
+ }
+ }
+ string_delete(&temp);
+ if (!success)
+ {
+ break;
+ }
+ }
+ /* z for template parameters */
+ else if (**mangled == 'z')
+ {
+ int r2;
+ (*mangled)++;
+ success = demangle_template_template_parm (work, mangled, tname);
+
+ if (success
+ && (r2 = consume_count (mangled)) > 0
+ && (int) strlen (*mangled) >= r2)
+ {
+ string_append (tname, " ");
+ string_appendn (tname, *mangled, r2);
+ if (!is_type)
+ {
+ /* Save the template argument. */
+ int len = r2;
+ work->tmpl_argvec[i] = g_malloc (len + 1);
+ memcpy (work->tmpl_argvec[i], *mangled, len);
+ work->tmpl_argvec[i][len] = '\0';
+ }
+ *mangled += r2;
+ }
+ if (!success)
+ {
+ break;
+ }
+ }
+ else
+ {
+ string param;
+ string* s;
+
+ /* otherwise, value parameter */
+
+ /* temp is initialized in do_type */
+ success = do_type (work, mangled, &temp);
+ string_delete(&temp);
+ if (!success)
+ break;
+
+ if (!is_type)
+ {
+ s = &param;
+ string_init (s);
+ }
+ else
+ s = tname;
+
+ success = demangle_template_value_parm (work, mangled, s,
+ (type_kind_t) success);
+
+ if (!success)
+ {
+ if (!is_type)
+ string_delete (s);
+ success = 0;
+ break;
+ }
+
+ if (!is_type)
+ {
+ int len = s->p - s->b;
+ work->tmpl_argvec[i] = g_malloc (len + 1);
+ memcpy (work->tmpl_argvec[i], s->b, len);
+ work->tmpl_argvec[i][len] = '\0';
+
+ string_appends (tname, s);
+ string_delete (s);
+ }
+ }
+ need_comma = 1;
+ }
+ if (is_java_array)
+ {
+ string_append (tname, "[]");
+ }
+ else
+ {
+ if (tname->p[-1] == '>')
+ string_append (tname, " ");
+ string_append (tname, ">");
+ }
+
+ if (is_type && remember)
+ {
+ const int bindex = register_Btype (work);
+ remember_Btype (work, tname->b, LEN_STRING (tname), bindex);
+ }
+
+ /*
+ if (work -> static_type)
+ {
+ string_append (declp, *mangled + 1);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ else
+ {
+ success = demangle_args (work, mangled, declp);
+ }
+ }
+ */
+ return (success);
+}
+
+static int
+arm_pt (work, mangled, n, anchor, args)
+ struct work_stuff *work;
+ const char *mangled;
+ int n;
+ const char **anchor, **args;
+{
+ /* Check if ARM template with "__pt__" in it ("parameterized type") */
+ /* Allow HP also here, because HP's cfront compiler follows ARM to some extent */
+ if ((ARM_DEMANGLING || HP_DEMANGLING) && (*anchor = strstr (mangled, "__pt__")))
+ {
+ int len;
+ *args = *anchor + 6;
+ len = consume_count (args);
+ if (len == -1)
+ return 0;
+ if (*args + len == mangled + n && **args == '_')
+ {
+ ++*args;
+ return 1;
+ }
+ }
+ if (AUTO_DEMANGLING || EDG_DEMANGLING)
+ {
+ if ((*anchor = strstr (mangled, "__tm__"))
+ || (*anchor = strstr (mangled, "__ps__"))
+ || (*anchor = strstr (mangled, "__pt__")))
+ {
+ int len;
+ *args = *anchor + 6;
+ len = consume_count (args);
+ if (len == -1)
+ return 0;
+ if (*args + len == mangled + n && **args == '_')
+ {
+ ++*args;
+ return 1;
+ }
+ }
+ else if ((*anchor = strstr (mangled, "__S")))
+ {
+ int len;
+ *args = *anchor + 3;
+ len = consume_count (args);
+ if (len == -1)
+ return 0;
+ if (*args + len == mangled + n && **args == '_')
+ {
+ ++*args;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void
+demangle_arm_hp_template (work, mangled, n, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ int n;
+ string *declp;
+{
+ const char *p;
+ const char *args;
+ const char *e = *mangled + n;
+ string arg;
+
+ /* Check for HP aCC template spec: classXt1t2 where t1, t2 are
+ template args */
+ if (HP_DEMANGLING && ((*mangled)[n] == 'X'))
+ {
+ char *start_spec_args = NULL;
+ int hold_options;
+
+ /* First check for and omit template specialization pseudo-arguments,
+ such as in "Spec<#1,#1.*>" */
+ start_spec_args = strchr (*mangled, '<');
+ if (start_spec_args && (start_spec_args - *mangled < n))
+ string_appendn (declp, *mangled, start_spec_args - *mangled);
+ else
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n + 1;
+ string_init (&arg);
+ if (work->temp_start == -1) /* non-recursive call */
+ work->temp_start = declp->p - declp->b;
+
+ /* We want to unconditionally demangle parameter types in
+ template parameters. */
+ hold_options = work->options;
+ work->options |= DMGL_PARAMS;
+
+ string_append (declp, "<");
+ while (1)
+ {
+ string_delete (&arg);
+ switch (**mangled)
+ {
+ case 'T':
+ /* 'T' signals a type parameter */
+ (*mangled)++;
+ if (!do_type (work, mangled, &arg))
+ goto hpacc_template_args_done;
+ break;
+
+ case 'U':
+ case 'S':
+ /* 'U' or 'S' signals an integral value */
+ if (!do_hpacc_template_const_value (work, mangled, &arg))
+ goto hpacc_template_args_done;
+ break;
+
+ case 'A':
+ /* 'A' signals a named constant expression (literal) */
+ if (!do_hpacc_template_literal (work, mangled, &arg))
+ goto hpacc_template_args_done;
+ break;
+
+ default:
+ /* Today, 1997-09-03, we have only the above types
+ of template parameters */
+ /* FIXME: maybe this should fail and return null */
+ goto hpacc_template_args_done;
+ }
+ string_appends (declp, &arg);
+ /* Check if we're at the end of template args.
+ 0 if at end of static member of template class,
+ _ if done with template args for a function */
+ if ((**mangled == '\000') || (**mangled == '_'))
+ break;
+ else
+ string_append (declp, ",");
+ }
+ hpacc_template_args_done:
+ string_append (declp, ">");
+ string_delete (&arg);
+ if (**mangled == '_')
+ (*mangled)++;
+ work->options = hold_options;
+ return;
+ }
+ /* ARM template? (Also handles HP cfront extensions) */
+ else if (arm_pt (work, *mangled, n, &p, &args))
+ {
+ int hold_options;
+ string type_str;
+
+ string_init (&arg);
+ string_appendn (declp, *mangled, p - *mangled);
+ if (work->temp_start == -1) /* non-recursive call */
+ work->temp_start = declp->p - declp->b;
+
+ /* We want to unconditionally demangle parameter types in
+ template parameters. */
+ hold_options = work->options;
+ work->options |= DMGL_PARAMS;
+
+ string_append (declp, "<");
+ /* should do error checking here */
+ while (args < e) {
+ string_delete (&arg);
+
+ /* Check for type or literal here */
+ switch (*args)
+ {
+ /* HP cfront extensions to ARM for template args */
+ /* spec: Xt1Lv1 where t1 is a type, v1 is a literal value */
+ /* FIXME: We handle only numeric literals for HP cfront */
+ case 'X':
+ /* A typed constant value follows */
+ args++;
+ if (!do_type (work, &args, &type_str))
+ goto cfront_template_args_done;
+ string_append (&arg, "(");
+ string_appends (&arg, &type_str);
+ string_delete (&type_str);
+ string_append (&arg, ")");
+ if (*args != 'L')
+ goto cfront_template_args_done;
+ args++;
+ /* Now snarf a literal value following 'L' */
+ if (!snarf_numeric_literal (&args, &arg))
+ goto cfront_template_args_done;
+ break;
+
+ case 'L':
+ /* Snarf a literal following 'L' */
+ args++;
+ if (!snarf_numeric_literal (&args, &arg))
+ goto cfront_template_args_done;
+ break;
+ default:
+ /* Not handling other HP cfront stuff */
+ {
+ const char* old_args = args;
+ if (!do_type (work, &args, &arg))
+ goto cfront_template_args_done;
+
+ /* Fail if we didn't make any progress: prevent infinite loop. */
+ if (args == old_args)
+ {
+ work->options = hold_options;
+ return;
+ }
+ }
+ }
+ string_appends (declp, &arg);
+ string_append (declp, ",");
+ }
+ cfront_template_args_done:
+ string_delete (&arg);
+ if (args >= e)
+ --declp->p; /* remove extra comma */
+ string_append (declp, ">");
+ work->options = hold_options;
+ }
+ else if (n>10 && strncmp (*mangled, "_GLOBAL_", 8) == 0
+ && (*mangled)[9] == 'N'
+ && (*mangled)[8] == (*mangled)[10]
+ && strchr (cplus_markers, (*mangled)[8]))
+ {
+ /* A member of the anonymous namespace. */
+ string_append (declp, "{anonymous}");
+ }
+ else
+ {
+ if (work->temp_start == -1) /* non-recursive call only */
+ work->temp_start = 0; /* disable in recursive calls */
+ string_appendn (declp, *mangled, n);
+ }
+ *mangled += n;
+}
+
+/* Extract a class name, possibly a template with arguments, from the
+ mangled string; qualifiers, local class indicators, etc. have
+ already been dealt with */
+
+static int
+demangle_class_name (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ int n;
+ int success = 0;
+
+ n = consume_count (mangled);
+ if (n == -1)
+ return 0;
+ if ((int) strlen (*mangled) >= n)
+ {
+ demangle_arm_hp_template (work, mangled, n, declp);
+ success = 1;
+ }
+
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_class -- demangle a mangled class sequence
+
+SYNOPSIS
+
+ static int
+ demangle_class (struct work_stuff *work, const char **mangled,
+ strint *declp)
+
+DESCRIPTION
+
+ DECLP points to the buffer into which demangling is being done.
+
+ *MANGLED points to the current token to be demangled. On input,
+ it points to a mangled class (I.E. "3foo", "13verylongclass", etc.)
+ On exit, it points to the next token after the mangled class on
+ success, or the first unconsumed token on failure.
+
+ If the CONSTRUCTOR or DESTRUCTOR flags are set in WORK, then
+ we are demangling a constructor or destructor. In this case
+ we prepend "class::class" or "class::~class" to DECLP.
+
+ Otherwise, we prepend "class::" to the current DECLP.
+
+ Reset the constructor/destructor flags once they have been
+ "consumed". This allows demangle_class to be called later during
+ the same demangling, to do normal class demangling.
+
+ Returns 1 if demangling is successful, 0 otherwise.
+
+*/
+
+static int
+demangle_class (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ int success = 0;
+ int btype;
+ string class_name;
+ char *save_class_name_end = 0;
+
+ string_init (&class_name);
+ btype = register_Btype (work);
+ if (demangle_class_name (work, mangled, &class_name))
+ {
+ save_class_name_end = class_name.p;
+ if ((work->constructor & 1) || (work->destructor & 1))
+ {
+ /* adjust so we don't include template args */
+ if (work->temp_start && (work->temp_start != -1))
+ {
+ class_name.p = class_name.b + work->temp_start;
+ }
+ string_prepends (declp, &class_name);
+ if (work -> destructor & 1)
+ {
+ string_prepend (declp, "~");
+ work -> destructor -= 1;
+ }
+ else
+ {
+ work -> constructor -= 1;
+ }
+ }
+ class_name.p = save_class_name_end;
+ remember_Ktype (work, class_name.b, LEN_STRING(&class_name));
+ remember_Btype (work, class_name.b, LEN_STRING(&class_name), btype);
+ string_prepend (declp, SCOPE_STRING (work));
+ string_prepends (declp, &class_name);
+ success = 1;
+ }
+ string_delete (&class_name);
+ return (success);
+}
+
+
+/* Called when there's a "__" in the mangled name, with `scan' pointing to
+ the rightmost guess.
+
+ Find the correct "__"-sequence where the function name ends and the
+ signature starts, which is ambiguous with GNU mangling.
+ Call demangle_signature here, so we can make sure we found the right
+ one; *mangled will be consumed so caller will not make further calls to
+ demangle_signature. */
+
+static int
+iterate_demangle_function (work, mangled, declp, scan)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+ const char *scan;
+{
+ const char *mangle_init = *mangled;
+ int success = 0;
+ string decl_init;
+ struct work_stuff work_init;
+
+ if (*(scan + 2) == '\0')
+ return 0;
+
+ /* Do not iterate for some demangling modes, or if there's only one
+ "__"-sequence. This is the normal case. */
+ if (ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING
+ || strstr (scan + 2, "__") == NULL)
+ {
+ demangle_function_name (work, mangled, declp, scan);
+ return 1;
+ }
+
+ /* Save state so we can restart if the guess at the correct "__" was
+ wrong. */
+ string_init (&decl_init);
+ string_appends (&decl_init, declp);
+ memset (&work_init, 0, sizeof work_init);
+ work_stuff_copy_to_from (&work_init, work);
+
+ /* Iterate over occurrences of __, allowing names and types to have a
+ "__" sequence in them. We must start with the first (not the last)
+ occurrence, since "__" most often occur between independent mangled
+ parts, hence starting at the last occurence inside a signature
+ might get us a "successful" demangling of the signature. */
+
+ while (scan[2])
+ {
+ demangle_function_name (work, mangled, declp, scan);
+ success = demangle_signature (work, mangled, declp);
+ if (success)
+ break;
+
+ /* Reset demangle state for the next round. */
+ *mangled = mangle_init;
+ string_clear (declp);
+ string_appends (declp, &decl_init);
+ work_stuff_copy_to_from (work, &work_init);
+
+ /* Leave this underscore-sequence. */
+ scan += 2;
+
+ /* Scan for the next "__" sequence. */
+ while (*scan && (scan[0] != '_' || scan[1] != '_'))
+ scan++;
+
+ /* Move to last "__" in this sequence. */
+ while (*scan && *scan == '_')
+ scan++;
+ scan -= 2;
+ }
+
+ /* Delete saved state. */
+ delete_work_stuff (&work_init);
+ string_delete (&decl_init);
+
+ return success;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_prefix -- consume the mangled name prefix and find signature
+
+SYNOPSIS
+
+ static int
+ demangle_prefix (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+DESCRIPTION
+
+ Consume and demangle the prefix of the mangled name.
+ While processing the function name root, arrange to call
+ demangle_signature if the root is ambiguous.
+
+ DECLP points to the string buffer into which demangled output is
+ placed. On entry, the buffer is empty. On exit it contains
+ the root function name, the demangled operator name, or in some
+ special cases either nothing or the completely demangled result.
+
+ MANGLED points to the current pointer into the mangled name. As each
+ token of the mangled name is consumed, it is updated. Upon entry
+ the current mangled name pointer points to the first character of
+ the mangled name. Upon exit, it should point to the first character
+ of the signature if demangling was successful, or to the first
+ unconsumed character if demangling of the prefix was unsuccessful.
+
+ Returns 1 on success, 0 otherwise.
+ */
+
+static int
+demangle_prefix (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ int success = 1;
+ const char *scan;
+ int i;
+
+ if (strlen(*mangled) > 6
+ && (strncmp(*mangled, "_imp__", 6) == 0
+ || strncmp(*mangled, "__imp_", 6) == 0))
+ {
+ /* it's a symbol imported from a PE dynamic library. Check for both
+ new style prefix _imp__ and legacy __imp_ used by older versions
+ of dlltool. */
+ (*mangled) += 6;
+ work->dllimported = 1;
+ }
+ else if (strlen(*mangled) >= 11 && strncmp(*mangled, "_GLOBAL_", 8) == 0)
+ {
+ char *marker = strchr (cplus_markers, (*mangled)[8]);
+ if (marker != NULL && *marker == (*mangled)[10])
+ {
+ if ((*mangled)[9] == 'D')
+ {
+ /* it's a GNU global destructor to be executed at program exit */
+ (*mangled) += 11;
+ work->destructor = 2;
+ if (gnu_special (work, mangled, declp))
+ return success;
+ }
+ else if ((*mangled)[9] == 'I')
+ {
+ /* it's a GNU global constructor to be executed at program init */
+ (*mangled) += 11;
+ work->constructor = 2;
+ if (gnu_special (work, mangled, declp))
+ return success;
+ }
+ }
+ }
+ else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__std__", 7) == 0)
+ {
+ /* it's a ARM global destructor to be executed at program exit */
+ (*mangled) += 7;
+ work->destructor = 2;
+ }
+ else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__sti__", 7) == 0)
+ {
+ /* it's a ARM global constructor to be executed at program initial */
+ (*mangled) += 7;
+ work->constructor = 2;
+ }
+
+ /* This block of code is a reduction in strength time optimization
+ of:
+ scan = strstr (*mangled, "__"); */
+
+ {
+ scan = *mangled;
+
+ do {
+ scan = strchr (scan, '_');
+ } while (scan != NULL && *++scan != '_');
+
+ if (scan != NULL) --scan;
+ }
+
+ if (scan != NULL)
+ {
+ /* We found a sequence of two or more '_', ensure that we start at
+ the last pair in the sequence. */
+ i = strspn (scan, "_");
+ if (i > 2)
+ {
+ scan += (i - 2);
+ }
+ }
+
+ if (scan == NULL)
+ {
+ success = 0;
+ }
+ else if (work -> static_type)
+ {
+ if (!g_ascii_isdigit ((unsigned char)scan[0]) && (scan[0] != 't'))
+ {
+ success = 0;
+ }
+ }
+ else if ((scan == *mangled)
+ && (g_ascii_isdigit ((unsigned char)scan[2]) || (scan[2] == 'Q')
+ || (scan[2] == 't') || (scan[2] == 'K') || (scan[2] == 'H')))
+ {
+ /* The ARM says nothing about the mangling of local variables.
+ But cfront mangles local variables by prepending __<nesting_level>
+ to them. As an extension to ARM demangling we handle this case. */
+ if ((LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING)
+ && g_ascii_isdigit ((unsigned char)scan[2]))
+ {
+ *mangled = scan + 2;
+ consume_count (mangled);
+ string_append (declp, *mangled);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ else
+ {
+ /* A GNU style constructor starts with __[0-9Qt]. But cfront uses
+ names like __Q2_3foo3bar for nested type names. So don't accept
+ this style of constructor for cfront demangling. A GNU
+ style member-template constructor starts with 'H'. */
+ if (!(LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING))
+ work -> constructor += 1;
+ *mangled = scan + 2;
+ }
+ }
+ else if (ARM_DEMANGLING && scan[2] == 'p' && scan[3] == 't')
+ {
+ /* Cfront-style parameterized type. Handled later as a signature. */
+ success = 1;
+
+ /* ARM template? */
+ demangle_arm_hp_template (work, mangled, strlen (*mangled), declp);
+ }
+ else if (EDG_DEMANGLING && ((scan[2] == 't' && scan[3] == 'm')
+ || (scan[2] == 'p' && scan[3] == 's')
+ || (scan[2] == 'p' && scan[3] == 't')))
+ {
+ /* EDG-style parameterized type. Handled later as a signature. */
+ success = 1;
+
+ /* EDG template? */
+ demangle_arm_hp_template (work, mangled, strlen (*mangled), declp);
+ }
+ else if ((scan == *mangled) && !g_ascii_isdigit ((unsigned char)scan[2])
+ && (scan[2] != 't'))
+ {
+ /* Mangled name starts with "__". Skip over any leading '_' characters,
+ then find the next "__" that separates the prefix from the signature.
+ */
+ if (!(ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
+ || (arm_special (mangled, declp) == 0))
+ {
+ while (*scan == '_')
+ {
+ scan++;
+ }
+ if ((scan = strstr (scan, "__")) == NULL || (*(scan + 2) == '\0'))
+ {
+ /* No separator (I.E. "__not_mangled"), or empty signature
+ (I.E. "__not_mangled_either__") */
+ success = 0;
+ }
+ else
+ return iterate_demangle_function (work, mangled, declp, scan);
+ }
+ }
+ else if (*(scan + 2) != '\0')
+ {
+ /* Mangled name does not start with "__" but does have one somewhere
+ in there with non empty stuff after it. Looks like a global
+ function name. Iterate over all "__":s until the right
+ one is found. */
+ return iterate_demangle_function (work, mangled, declp, scan);
+ }
+ else
+ {
+ /* Doesn't look like a mangled name */
+ success = 0;
+ }
+
+ if (!success && (work->constructor == 2 || work->destructor == 2))
+ {
+ string_append (declp, *mangled);
+ *mangled += strlen (*mangled);
+ success = 1;
+ }
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ gnu_special -- special handling of gnu mangled strings
+
+SYNOPSIS
+
+ static int
+ gnu_special (struct work_stuff *work, const char **mangled,
+ string *declp);
+
+
+DESCRIPTION
+
+ Process some special GNU style mangling forms that don't fit
+ the normal pattern. For example:
+
+ _$_3foo (destructor for class foo)
+ _vt$foo (foo virtual table)
+ _vt$foo$bar (foo::bar virtual table)
+ __vt_foo (foo virtual table, new style with thunks)
+ _3foo$varname (static data member)
+ _Q22rs2tu$vw (static data member)
+ __t6vector1Zii (constructor with template)
+ __thunk_4__$_7ostream (virtual function thunk)
+ */
+
+static int
+gnu_special (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ int n;
+ int success = 1;
+ const char *p;
+
+ if ((*mangled)[0] == '_'
+ && strchr (cplus_markers, (*mangled)[1]) != NULL
+ && (*mangled)[2] == '_')
+ {
+ /* Found a GNU style destructor, get past "_<CPLUS_MARKER>_" */
+ (*mangled) += 3;
+ work -> destructor += 1;
+ }
+ else if ((*mangled)[0] == '_'
+ && (((*mangled)[1] == '_'
+ && (*mangled)[2] == 'v'
+ && (*mangled)[3] == 't'
+ && (*mangled)[4] == '_')
+ || ((*mangled)[1] == 'v'
+ && (*mangled)[2] == 't'
+ && strchr (cplus_markers, (*mangled)[3]) != NULL)))
+ {
+ /* Found a GNU style virtual table, get past "_vt<CPLUS_MARKER>"
+ and create the decl. Note that we consume the entire mangled
+ input string, which means that demangle_signature has no work
+ to do. */
+ if ((*mangled)[2] == 'v')
+ (*mangled) += 5; /* New style, with thunks: "__vt_" */
+ else
+ (*mangled) += 4; /* Old style, no thunks: "_vt<CPLUS_MARKER>" */
+ while (**mangled != '\0')
+ {
+ switch (**mangled)
+ {
+ case 'Q':
+ case 'K':
+ success = demangle_qualified (work, mangled, declp, 0, 1);
+ break;
+ case 't':
+ success = demangle_template (work, mangled, declp, 0, 1,
+ 1);
+ break;
+ default:
+ if (g_ascii_isdigit((unsigned char)*mangled[0]))
+ {
+ n = consume_count(mangled);
+ /* We may be seeing a too-large size, or else a
+ ".<digits>" indicating a static local symbol. In
+ any case, declare victory and move on; *don't* try
+ to use n to allocate. */
+ if (n > (int) strlen (*mangled))
+ {
+ success = 1;
+ break;
+ }
+ }
+ else
+ {
+ n = strcspn (*mangled, cplus_markers);
+ }
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n;
+ }
+
+ p = strpbrk (*mangled, cplus_markers);
+ if (success && ((p == NULL) || (p == *mangled)))
+ {
+ if (p != NULL)
+ {
+ string_append (declp, SCOPE_STRING (work));
+ (*mangled)++;
+ }
+ }
+ else
+ {
+ success = 0;
+ break;
+ }
+ }
+ if (success)
+ string_append (declp, " virtual table");
+ }
+ else if ((*mangled)[0] == '_'
+ && (strchr("0123456789Qt", (*mangled)[1]) != NULL)
+ && (p = strpbrk (*mangled, cplus_markers)) != NULL)
+ {
+ /* static data member, "_3foo$varname" for example */
+ (*mangled)++;
+ switch (**mangled)
+ {
+ case 'Q':
+ case 'K':
+ success = demangle_qualified (work, mangled, declp, 0, 1);
+ break;
+ case 't':
+ success = demangle_template (work, mangled, declp, 0, 1, 1);
+ break;
+ default:
+ n = consume_count (mangled);
+ if (n < 0 || n > (long) strlen (*mangled))
+ {
+ success = 0;
+ break;
+ }
+
+ if (n > 10 && strncmp (*mangled, "_GLOBAL_", 8) == 0
+ && (*mangled)[9] == 'N'
+ && (*mangled)[8] == (*mangled)[10]
+ && strchr (cplus_markers, (*mangled)[8]))
+ {
+ /* A member of the anonymous namespace. There's information
+ about what identifier or filename it was keyed to, but
+ it's just there to make the mangled name unique; we just
+ step over it. */
+ string_append (declp, "{anonymous}");
+ (*mangled) += n;
+
+ /* Now p points to the marker before the N, so we need to
+ update it to the first marker after what we consumed. */
+ p = strpbrk (*mangled, cplus_markers);
+ break;
+ }
+
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n;
+ }
+ if (success && (p == *mangled))
+ {
+ /* Consumed everything up to the cplus_marker, append the
+ variable name. */
+ (*mangled)++;
+ string_append (declp, SCOPE_STRING (work));
+ n = strlen (*mangled);
+ string_appendn (declp, *mangled, n);
+ (*mangled) += n;
+ }
+ else
+ {
+ success = 0;
+ }
+ }
+ else if (strncmp (*mangled, "__thunk_", 8) == 0)
+ {
+ int delta;
+
+ (*mangled) += 8;
+ delta = consume_count (mangled);
+ if (delta == -1)
+ success = 0;
+ else
+ {
+ char *method = internal_cplus_demangle (work, ++*mangled);
+
+ if (method)
+ {
+ char buf[50];
+ sprintf (buf, "virtual function thunk (delta:%d) for ", -delta);
+ string_append (declp, buf);
+ string_append (declp, method);
+ g_free (method);
+ n = strlen (*mangled);
+ (*mangled) += n;
+ }
+ else
+ {
+ success = 0;
+ }
+ }
+ }
+ else if (strncmp (*mangled, "__t", 3) == 0
+ && ((*mangled)[3] == 'i' || (*mangled)[3] == 'f'))
+ {
+ p = (*mangled)[3] == 'i' ? " type_info node" : " type_info function";
+ (*mangled) += 4;
+ switch (**mangled)
+ {
+ case 'Q':
+ case 'K':
+ success = demangle_qualified (work, mangled, declp, 0, 1);
+ break;
+ case 't':
+ success = demangle_template (work, mangled, declp, 0, 1, 1);
+ break;
+ default:
+ success = do_type (work, mangled, declp);
+ break;
+ }
+ if (success && **mangled != '\0')
+ success = 0;
+ if (success)
+ string_append (declp, p);
+ }
+ else
+ {
+ success = 0;
+ }
+ return (success);
+}
+
+static void
+recursively_demangle(work, mangled, result, namelength)
+ struct work_stuff *work;
+ const char **mangled;
+ string *result;
+ int namelength;
+{
+ char * recurse = (char *)NULL;
+ char * recurse_dem = (char *)NULL;
+
+ recurse = (char *) g_malloc (namelength + 1);
+ memcpy (recurse, *mangled, namelength);
+ recurse[namelength] = '\000';
+
+ recurse_dem = sysprof_cplus_demangle (recurse, work->options);
+
+ if (recurse_dem)
+ {
+ string_append (result, recurse_dem);
+ g_free (recurse_dem);
+ }
+ else
+ {
+ string_appendn (result, *mangled, namelength);
+ }
+ g_free (recurse);
+ *mangled += namelength;
+}
+
+/*
+
+LOCAL FUNCTION
+
+ arm_special -- special handling of ARM/lucid mangled strings
+
+SYNOPSIS
+
+ static int
+ arm_special (const char **mangled,
+ string *declp);
+
+
+DESCRIPTION
+
+ Process some special ARM style mangling forms that don't fit
+ the normal pattern. For example:
+
+ __vtbl__3foo (foo virtual table)
+ __vtbl__3foo__3bar (bar::foo virtual table)
+
+ */
+
+static int
+arm_special (mangled, declp)
+ const char **mangled;
+ string *declp;
+{
+ int n;
+ int success = 1;
+ const char *scan;
+
+ if (strncmp (*mangled, ARM_VTABLE_STRING, ARM_VTABLE_STRLEN) == 0)
+ {
+ /* Found a ARM style virtual table, get past ARM_VTABLE_STRING
+ and create the decl. Note that we consume the entire mangled
+ input string, which means that demangle_signature has no work
+ to do. */
+ scan = *mangled + ARM_VTABLE_STRLEN;
+ while (*scan != '\0') /* first check it can be demangled */
+ {
+ n = consume_count (&scan);
+ if (n == -1)
+ {
+ return (0); /* no good */
+ }
+ scan += n;
+ if (scan[0] == '_' && scan[1] == '_')
+ {
+ scan += 2;
+ }
+ }
+ (*mangled) += ARM_VTABLE_STRLEN;
+ while (**mangled != '\0')
+ {
+ n = consume_count (mangled);
+ if (n == -1
+ || n > (long) strlen (*mangled))
+ return 0;
+ string_prependn (declp, *mangled, n);
+ (*mangled) += n;
+ if ((*mangled)[0] == '_' && (*mangled)[1] == '_')
+ {
+ string_prepend (declp, "::");
+ (*mangled) += 2;
+ }
+ }
+ string_append (declp, " virtual table");
+ }
+ else
+ {
+ success = 0;
+ }
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ demangle_qualified -- demangle 'Q' qualified name strings
+
+SYNOPSIS
+
+ static int
+ demangle_qualified (struct work_stuff *, const char *mangled,
+ string *result, int isfuncname, int append);
+
+DESCRIPTION
+
+ Demangle a qualified name, such as "Q25Outer5Inner" which is
+ the mangled form of "Outer::Inner". The demangled output is
+ prepended or appended to the result string according to the
+ state of the append flag.
+
+ If isfuncname is nonzero, then the qualified name we are building
+ is going to be used as a member function name, so if it is a
+ constructor or destructor function, append an appropriate
+ constructor or destructor name. I.E. for the above example,
+ the result for use as a constructor is "Outer::Inner::Inner"
+ and the result for use as a destructor is "Outer::Inner::~Inner".
+
+BUGS
+
+ Numeric conversion is ASCII dependent (FIXME).
+
+ */
+
+static int
+demangle_qualified (work, mangled, result, isfuncname, append)
+ struct work_stuff *work;
+ const char **mangled;
+ string *result;
+ int isfuncname;
+ int append;
+{
+ int qualifiers = 0;
+ int success = 1;
+ char num[2];
+ string temp;
+ string last_name;
+ int bindex = register_Btype (work);
+
+ /* We only make use of ISFUNCNAME if the entity is a constructor or
+ destructor. */
+ isfuncname = (isfuncname
+ && ((work->constructor & 1) || (work->destructor & 1)));
+
+ string_init (&temp);
+ string_init (&last_name);
+
+ if ((*mangled)[0] == 'K')
+ {
+ /* Squangling qualified name reuse */
+ int idx;
+ (*mangled)++;
+ idx = consume_count_with_underscores (mangled);
+ if (idx == -1 || idx >= work -> numk)
+ success = 0;
+ else
+ string_append (&temp, work -> ktypevec[idx]);
+ }
+ else
+ switch ((*mangled)[1])
+ {
+ case '_':
+ /* GNU mangled name with more than 9 classes. The count is preceded
+ by an underscore (to distinguish it from the <= 9 case) and followed
+ by an underscore. */
+ (*mangled)++;
+ qualifiers = consume_count_with_underscores (mangled);
+ if (qualifiers == -1)
+ success = 0;
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* The count is in a single digit. */
+ num[0] = (*mangled)[1];
+ num[1] = '\0';
+ qualifiers = atoi (num);
+
+ /* If there is an underscore after the digit, skip it. This is
+ said to be for ARM-qualified names, but the ARM makes no
+ mention of such an underscore. Perhaps cfront uses one. */
+ if ((*mangled)[2] == '_')
+ {
+ (*mangled)++;
+ }
+ (*mangled) += 2;
+ break;
+
+ case '0':
+ default:
+ success = 0;
+ }
+
+ if (!success)
+ return success;
+
+ /* Pick off the names and collect them in the temp buffer in the order
+ in which they are found, separated by '::'. */
+
+ while (qualifiers-- > 0)
+ {
+ int remember_K = 1;
+ string_clear (&last_name);
+
+ if (*mangled[0] == '_')
+ (*mangled)++;
+
+ if (*mangled[0] == 't')
+ {
+ /* Here we always append to TEMP since we will want to use
+ the template name without the template parameters as a
+ constructor or destructor name. The appropriate
+ (parameter-less) value is returned by demangle_template
+ in LAST_NAME. We do not remember the template type here,
+ in order to match the G++ mangling algorithm. */
+ success = demangle_template(work, mangled, &temp,
+ &last_name, 1, 0);
+ if (!success)
+ break;
+ }
+ else if (*mangled[0] == 'K')
+ {
+ int idx;
+ (*mangled)++;
+ idx = consume_count_with_underscores (mangled);
+ if (idx == -1 || idx >= work->numk)
+ success = 0;
+ else
+ string_append (&temp, work->ktypevec[idx]);
+ remember_K = 0;
+
+ if (!success) break;
+ }
+ else
+ {
+ if (EDG_DEMANGLING)
+ {
+ int namelength;
+ /* Now recursively demangle the qualifier
+ * This is necessary to deal with templates in
+ * mangling styles like EDG */
+ namelength = consume_count (mangled);
+ if (namelength == -1)
+ {
+ success = 0;
+ break;
+ }
+ recursively_demangle(work, mangled, &temp, namelength);
+ }
+ else
+ {
+ string_delete (&last_name);
+ success = do_type (work, mangled, &last_name);
+ if (!success)
+ break;
+ string_appends (&temp, &last_name);
+ }
+ }
+
+ if (remember_K)
+ remember_Ktype (work, temp.b, LEN_STRING (&temp));
+
+ if (qualifiers > 0)
+ string_append (&temp, SCOPE_STRING (work));
+ }
+
+ remember_Btype (work, temp.b, LEN_STRING (&temp), bindex);
+
+ /* If we are using the result as a function name, we need to append
+ the appropriate '::' separated constructor or destructor name.
+ We do this here because this is the most convenient place, where
+ we already have a pointer to the name and the length of the name. */
+
+ if (isfuncname)
+ {
+ string_append (&temp, SCOPE_STRING (work));
+ if (work -> destructor & 1)
+ string_append (&temp, "~");
+ string_appends (&temp, &last_name);
+ }
+
+ /* Now either prepend the temp buffer to the result, or append it,
+ depending upon the state of the append flag. */
+
+ if (append)
+ string_appends (result, &temp);
+ else
+ {
+ if (!STRING_EMPTY (result))
+ string_append (&temp, SCOPE_STRING (work));
+ string_prepends (result, &temp);
+ }
+
+ string_delete (&last_name);
+ string_delete (&temp);
+ return (success);
+}
+
+/*
+
+LOCAL FUNCTION
+
+ get_count -- convert an ascii count to integer, consuming tokens
+
+SYNOPSIS
+
+ static int
+ get_count (const char **type, int *count)
+
+DESCRIPTION
+
+ Assume that *type points at a count in a mangled name; set
+ *count to its value, and set *type to the next character after
+ the count. There are some weird rules in effect here.
+
+ If *type does not point at a string of digits, return zero.
+
+ If *type points at a string of digits followed by an
+ underscore, set *count to their value as an integer, advance
+ *type to point *after the underscore, and return 1.
+
+ If *type points at a string of digits not followed by an
+ underscore, consume only the first digit. Set *count to its
+ value as an integer, leave *type pointing after that digit,
+ and return 1.
+
+ The excuse for this odd behavior: in the ARM and HP demangling
+ styles, a type can be followed by a repeat count of the form
+ `Nxy', where:
+
+ `x' is a single digit specifying how many additional copies
+ of the type to append to the argument list, and
+
+ `y' is one or more digits, specifying the zero-based index of
+ the first repeated argument in the list. Yes, as you're
+ unmangling the name you can figure this out yourself, but
+ it's there anyway.
+
+ So, for example, in `bar__3fooFPiN51', the first argument is a
+ pointer to an integer (`Pi'), and then the next five arguments
+ are the same (`N5'), and the first repeat is the function's
+ second argument (`1').
+*/
+
+static int
+get_count (type, count)
+ const char **type;
+ int *count;
+{
+ const char *p;
+ int n;
+
+ if (!g_ascii_isdigit ((unsigned char)**type))
+ return (0);
+ else
+ {
+ *count = **type - '0';
+ (*type)++;
+ if (g_ascii_isdigit ((unsigned char)**type))
+ {
+ p = *type;
+ n = *count;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p++;
+ }
+ while (g_ascii_isdigit ((unsigned char)*p));
+ if (*p == '_')
+ {
+ *type = p + 1;
+ *count = n;
+ }
+ }
+ }
+ return (1);
+}
+
+/* RESULT will be initialised here; it will be freed on failure. The
+ value returned is really a type_kind_t. */
+
+static int
+do_type (work, mangled, result)
+ struct work_stuff *work;
+ const char **mangled;
+ string *result;
+{
+ int n;
+ int done;
+ int success;
+ string decl;
+ const char *remembered_type;
+ int type_quals;
+ type_kind_t tk = tk_none;
+
+ string_init (&decl);
+ string_init (result);
+
+ done = 0;
+ success = 1;
+ while (success && !done)
+ {
+ int member;
+ switch (**mangled)
+ {
+
+ /* A pointer type */
+ case 'P':
+ case 'p':
+ (*mangled)++;
+ if (! (work -> options & DMGL_JAVA))
+ string_prepend (&decl, "*");
+ if (tk == tk_none)
+ tk = tk_pointer;
+ break;
+
+ /* A reference type */
+ case 'R':
+ (*mangled)++;
+ string_prepend (&decl, "&");
+ if (tk == tk_none)
+ tk = tk_reference;
+ break;
+
+ /* An array */
+ case 'A':
+ {
+ ++(*mangled);
+ if (!STRING_EMPTY (&decl)
+ && (decl.b[0] == '*' || decl.b[0] == '&'))
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ string_append (&decl, "[");
+ if (**mangled != '_')
+ success = demangle_template_value_parm (work, mangled, &decl,
+ tk_integral);
+ if (**mangled == '_')
+ ++(*mangled);
+ string_append (&decl, "]");
+ break;
+ }
+
+ /* A back reference to a previously seen type */
+ case 'T':
+ (*mangled)++;
+ if (!get_count (mangled, &n) || n >= work -> ntypes)
+ {
+ success = 0;
+ }
+ else
+ {
+ remembered_type = work -> typevec[n];
+ mangled = &remembered_type;
+ }
+ break;
+
+ /* A function */
+ case 'F':
+ (*mangled)++;
+ if (!STRING_EMPTY (&decl)
+ && (decl.b[0] == '*' || decl.b[0] == '&'))
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ /* After picking off the function args, we expect to either find the
+ function return type (preceded by an '_') or the end of the
+ string. */
+ if (!demangle_nested_args (work, mangled, &decl)
+ || (**mangled != '_' && **mangled != '\0'))
+ {
+ success = 0;
+ break;
+ }
+ if (success && (**mangled == '_'))
+ (*mangled)++;
+ break;
+
+ case 'M':
+ case 'O':
+ {
+ type_quals = TYPE_UNQUALIFIED;
+
+ member = **mangled == 'M';
+ (*mangled)++;
+
+ string_append (&decl, ")");
+
+ /* We don't need to prepend `::' for a qualified name;
+ demangle_qualified will do that for us. */
+ if (**mangled != 'Q')
+ string_prepend (&decl, SCOPE_STRING (work));
+
+ if (g_ascii_isdigit ((unsigned char)**mangled))
+ {
+ n = consume_count (mangled);
+ if (n == -1
+ || (int) strlen (*mangled) < n)
+ {
+ success = 0;
+ break;
+ }
+ string_prependn (&decl, *mangled, n);
+ *mangled += n;
+ }
+ else if (**mangled == 'X' || **mangled == 'Y')
+ {
+ string temp;
+ do_type (work, mangled, &temp);
+ string_prepends (&decl, &temp);
+ string_delete (&temp);
+ }
+ else if (**mangled == 't')
+ {
+ string temp;
+ string_init (&temp);
+ success = demangle_template (work, mangled, &temp,
+ NULL, 1, 1);
+ if (success)
+ {
+ string_prependn (&decl, temp.b, temp.p - temp.b);
+ string_delete (&temp);
+ }
+ else
+ break;
+ }
+ else if (**mangled == 'Q')
+ {
+ success = demangle_qualified (work, mangled, &decl,
+ /*isfuncnam=*/0,
+ /*append=*/0);
+ if (!success)
+ break;
+ }
+ else
+ {
+ success = 0;
+ break;
+ }
+
+ string_prepend (&decl, "(");
+ if (member)
+ {
+ switch (**mangled)
+ {
+ case 'C':
+ case 'V':
+ case 'u':
+ type_quals |= code_for_qualifier (**mangled);
+ (*mangled)++;
+ break;
+
+ default:
+ break;
+ }
+
+ if (*(*mangled)++ != 'F')
+ {
+ success = 0;
+ break;
+ }
+ }
+ if ((member && !demangle_nested_args (work, mangled, &decl))
+ || **mangled != '_')
+ {
+ success = 0;
+ break;
+ }
+ (*mangled)++;
+ if (! PRINT_ANSI_QUALIFIERS)
+ {
+ break;
+ }
+ if (type_quals != TYPE_UNQUALIFIED)
+ {
+ APPEND_BLANK (&decl);
+ string_append (&decl, qualifier_string (type_quals));
+ }
+ break;
+ }
+ case 'G':
+ (*mangled)++;
+ break;
+
+ case 'C':
+ case 'V':
+ case 'u':
+ if (PRINT_ANSI_QUALIFIERS)
+ {
+ if (!STRING_EMPTY (&decl))
+ string_prepend (&decl, " ");
+
+ string_prepend (&decl, demangle_qualifier (**mangled));
+ }
+ (*mangled)++;
+ break;
+ /*
+ }
+ */
+
+ /* fall through */
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ if (success) switch (**mangled)
+ {
+ /* A qualified name, such as "Outer::Inner". */
+ case 'Q':
+ case 'K':
+ {
+ success = demangle_qualified (work, mangled, result, 0, 1);
+ break;
+ }
+
+ /* A back reference to a previously seen squangled type */
+ case 'B':
+ (*mangled)++;
+ if (!get_count (mangled, &n) || n >= work -> numb)
+ success = 0;
+ else
+ string_append (result, work->btypevec[n]);
+ break;
+
+ case 'X':
+ case 'Y':
+ /* A template parm. We substitute the corresponding argument. */
+ {
+ int idx;
+
+ (*mangled)++;
+ idx = consume_count_with_underscores (mangled);
+
+ if (idx == -1
+ || (work->tmpl_argvec && idx >= work->ntmpl_args)
+ || consume_count_with_underscores (mangled) == -1)
+ {
+ success = 0;
+ break;
+ }
+
+ if (work->tmpl_argvec)
+ string_append (result, work->tmpl_argvec[idx]);
+ else
+ string_append_template_idx (result, idx);
+
+ success = 1;
+ }
+ break;
+
+ default:
+ success = demangle_fund_type (work, mangled, result);
+ if (tk == tk_none)
+ tk = (type_kind_t) success;
+ break;
+ }
+
+ if (success)
+ {
+ if (!STRING_EMPTY (&decl))
+ {
+ string_append (result, " ");
+ string_appends (result, &decl);
+ }
+ }
+ else
+ string_delete (result);
+ string_delete (&decl);
+
+ if (success)
+ /* Assume an integral type, if we're not sure. */
+ return (int) ((tk == tk_none) ? tk_integral : tk);
+ else
+ return 0;
+}
+
+/* Given a pointer to a type string that represents a fundamental type
+ argument (int, long, unsigned int, etc) in TYPE, a pointer to the
+ string in which the demangled output is being built in RESULT, and
+ the WORK structure, decode the types and add them to the result.
+
+ For example:
+
+ "Ci" => "const int"
+ "Sl" => "signed long"
+ "CUs" => "const unsigned short"
+
+ The value returned is really a type_kind_t. */
+
+static int
+demangle_fund_type (work, mangled, result)
+ struct work_stuff *work;
+ const char **mangled;
+ string *result;
+{
+ int done = 0;
+ int success = 1;
+ char buf[10];
+ unsigned int dec = 0;
+ type_kind_t tk = tk_integral;
+
+ /* First pick off any type qualifiers. There can be more than one. */
+
+ while (!done)
+ {
+ switch (**mangled)
+ {
+ case 'C':
+ case 'V':
+ case 'u':
+ if (PRINT_ANSI_QUALIFIERS)
+ {
+ if (!STRING_EMPTY (result))
+ string_prepend (result, " ");
+ string_prepend (result, demangle_qualifier (**mangled));
+ }
+ (*mangled)++;
+ break;
+ case 'U':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "unsigned");
+ break;
+ case 'S': /* signed char only */
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "signed");
+ break;
+ case 'J':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "__complex");
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ /* Now pick off the fundamental type. There can be only one. */
+
+ switch (**mangled)
+ {
+ case '\0':
+ case '_':
+ break;
+ case 'v':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "void");
+ break;
+ case 'x':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "long long");
+ break;
+ case 'l':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "long");
+ break;
+ case 'i':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "int");
+ break;
+ case 's':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "short");
+ break;
+ case 'b':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "bool");
+ tk = tk_bool;
+ break;
+ case 'c':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "char");
+ tk = tk_char;
+ break;
+ case 'w':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "wchar_t");
+ tk = tk_char;
+ break;
+ case 'r':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "long double");
+ tk = tk_real;
+ break;
+ case 'd':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "double");
+ tk = tk_real;
+ break;
+ case 'f':
+ (*mangled)++;
+ APPEND_BLANK (result);
+ string_append (result, "float");
+ tk = tk_real;
+ break;
+ case 'G':
+ (*mangled)++;
+ if (!g_ascii_isdigit ((unsigned char)**mangled))
+ {
+ success = 0;
+ break;
+ }
+ case 'I':
+ (*mangled)++;
+ if (**mangled == '_')
+ {
+ int i;
+ (*mangled)++;
+ for (i = 0;
+ i < (long) sizeof (buf) - 1 && **mangled && **mangled != '_';
+ (*mangled)++, i++)
+ buf[i] = **mangled;
+ if (**mangled != '_')
+ {
+ success = 0;
+ break;
+ }
+ buf[i] = '\0';
+ (*mangled)++;
+ }
+ else
+ {
+ strncpy (buf, *mangled, 2);
+ buf[2] = '\0';
+ *mangled += min (strlen (*mangled), 2);
+ }
+ sscanf (buf, "%x", &dec);
+ sprintf (buf, "int%u_t", dec);
+ APPEND_BLANK (result);
+ string_append (result, buf);
+ break;
+
+ /* fall through */
+ /* An explicit type, such as "6mytype" or "7integer" */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ int bindex = register_Btype (work);
+ string btype;
+ string_init (&btype);
+ if (demangle_class_name (work, mangled, &btype)) {
+ remember_Btype (work, btype.b, LEN_STRING (&btype), bindex);
+ APPEND_BLANK (result);
+ string_appends (result, &btype);
+ }
+ else
+ success = 0;
+ string_delete (&btype);
+ break;
+ }
+ case 't':
+ {
+ string btype;
+ string_init (&btype);
+ success = demangle_template (work, mangled, &btype, 0, 1, 1);
+ string_appends (result, &btype);
+ string_delete (&btype);
+ break;
+ }
+ default:
+ success = 0;
+ break;
+ }
+
+ return success ? ((int) tk) : 0;
+}
+
+
+/* Handle a template's value parameter for HP aCC (extension from ARM)
+ **mangled points to 'S' or 'U' */
+
+static int
+do_hpacc_template_const_value (work, mangled, result)
+ struct work_stuff *work ATTRIBUTE_UNUSED;
+ const char **mangled;
+ string *result;
+{
+ int unsigned_const;
+
+ if (**mangled != 'U' && **mangled != 'S')
+ return 0;
+
+ unsigned_const = (**mangled == 'U');
+
+ (*mangled)++;
+
+ switch (**mangled)
+ {
+ case 'N':
+ string_append (result, "-");
+ /* fall through */
+ case 'P':
+ (*mangled)++;
+ break;
+ case 'M':
+ /* special case for -2^31 */
+ string_append (result, "-2147483648");
+ (*mangled)++;
+ return 1;
+ default:
+ return 0;
+ }
+
+ /* We have to be looking at an integer now */
+ if (!(g_ascii_isdigit ((unsigned char)**mangled)))
+ return 0;
+
+ /* We only deal with integral values for template
+ parameters -- so it's OK to look only for digits */
+ while (g_ascii_isdigit ((unsigned char)**mangled))
+ {
+ char_str[0] = **mangled;
+ string_append (result, char_str);
+ (*mangled)++;
+ }
+
+ if (unsigned_const)
+ string_append (result, "U");
+
+ /* FIXME? Some day we may have 64-bit (or larger :-) ) constants
+ with L or LL suffixes. pai/1997-09-03 */
+
+ return 1; /* success */
+}
+
+/* Handle a template's literal parameter for HP aCC (extension from ARM)
+ **mangled is pointing to the 'A' */
+
+static int
+do_hpacc_template_literal (work, mangled, result)
+ struct work_stuff *work;
+ const char **mangled;
+ string *result;
+{
+ int literal_len = 0;
+ char * recurse;
+ char * recurse_dem;
+
+ if (**mangled != 'A')
+ return 0;
+
+ (*mangled)++;
+
+ literal_len = consume_count (mangled);
+
+ if (literal_len <= 0)
+ return 0;
+
+ /* Literal parameters are names of arrays, functions, etc. and the
+ canonical representation uses the address operator */
+ string_append (result, "&");
+
+ /* Now recursively demangle the literal name */
+ recurse = (char *) g_malloc (literal_len + 1);
+ memcpy (recurse, *mangled, literal_len);
+ recurse[literal_len] = '\000';
+
+ recurse_dem = sysprof_cplus_demangle (recurse, work->options);
+
+ if (recurse_dem)
+ {
+ string_append (result, recurse_dem);
+ g_free (recurse_dem);
+ }
+ else
+ {
+ string_appendn (result, *mangled, literal_len);
+ }
+ (*mangled) += literal_len;
+ g_free (recurse);
+
+ return 1;
+}
+
+static int
+snarf_numeric_literal (args, arg)
+ const char ** args;
+ string * arg;
+{
+ if (**args == '-')
+ {
+ char_str[0] = '-';
+ string_append (arg, char_str);
+ (*args)++;
+ }
+ else if (**args == '+')
+ (*args)++;
+
+ if (!g_ascii_isdigit ((unsigned char)**args))
+ return 0;
+
+ while (g_ascii_isdigit ((unsigned char)**args))
+ {
+ char_str[0] = **args;
+ string_append (arg, char_str);
+ (*args)++;
+ }
+
+ return 1;
+}
+
+/* Demangle the next argument, given by MANGLED into RESULT, which
+ *should be an uninitialized* string. It will be initialized here,
+ and free'd should anything go wrong. */
+
+static int
+do_arg (work, mangled, result)
+ struct work_stuff *work;
+ const char **mangled;
+ string *result;
+{
+ /* Remember where we started so that we can record the type, for
+ non-squangling type remembering. */
+ const char *start = *mangled;
+
+ string_init (result);
+
+ if (work->nrepeats > 0)
+ {
+ --work->nrepeats;
+
+ if (work->previous_argument == 0)
+ return 0;
+
+ /* We want to reissue the previous type in this argument list. */
+ string_appends (result, work->previous_argument);
+ return 1;
+ }
+
+ if (**mangled == 'n')
+ {
+ /* A squangling-style repeat. */
+ (*mangled)++;
+ work->nrepeats = consume_count(mangled);
+
+ if (work->nrepeats <= 0)
+ /* This was not a repeat count after all. */
+ return 0;
+
+ if (work->nrepeats > 9)
+ {
+ if (**mangled != '_')
+ /* The repeat count should be followed by an '_' in this
+ case. */
+ return 0;
+ else
+ (*mangled)++;
+ }
+
+ /* Now, the repeat is all set up. */
+ return do_arg (work, mangled, result);
+ }
+
+ /* Save the result in WORK->previous_argument so that we can find it
+ if it's repeated. Note that saving START is not good enough: we
+ do not want to add additional types to the back-referenceable
+ type vector when processing a repeated type. */
+ if (work->previous_argument)
+ string_delete (work->previous_argument);
+ else
+ work->previous_argument = (string*) g_malloc (sizeof (string));
+
+ if (!do_type (work, mangled, work->previous_argument))
+ return 0;
+
+ string_appends (result, work->previous_argument);
+
+ remember_type (work, start, *mangled - start);
+ return 1;
+}
+
+static void
+remember_type (work, start, len)
+ struct work_stuff *work;
+ const char *start;
+ int len;
+{
+ char *tem;
+
+ if (work->forgetting_types)
+ return;
+
+ if (work -> ntypes >= work -> typevec_size)
+ {
+ if (work -> typevec_size == 0)
+ {
+ work -> typevec_size = 3;
+ work -> typevec
+ = (char **) g_malloc (sizeof (char *) * work -> typevec_size);
+ }
+ else
+ {
+ work -> typevec_size *= 2;
+ work -> typevec
+ = (char **) g_realloc ((char *)work -> typevec,
+ sizeof (char *) * work -> typevec_size);
+ }
+ }
+ tem = g_malloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ work -> typevec[work -> ntypes++] = tem;
+}
+
+
+/* Remember a K type class qualifier. */
+static void
+remember_Ktype (work, start, len)
+ struct work_stuff *work;
+ const char *start;
+ int len;
+{
+ char *tem;
+
+ if (work -> numk >= work -> ksize)
+ {
+ if (work -> ksize == 0)
+ {
+ work -> ksize = 5;
+ work -> ktypevec
+ = (char **) g_malloc (sizeof (char *) * work -> ksize);
+ }
+ else
+ {
+ work -> ksize *= 2;
+ work -> ktypevec
+ = (char **) g_realloc ((char *)work -> ktypevec,
+ sizeof (char *) * work -> ksize);
+ }
+ }
+ tem = g_malloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ work -> ktypevec[work -> numk++] = tem;
+}
+
+/* Register a B code, and get an index for it. B codes are registered
+ as they are seen, rather than as they are completed, so map<temp<char> >
+ registers map<temp<char> > as B0, and temp<char> as B1 */
+
+static int
+register_Btype (work)
+ struct work_stuff *work;
+{
+ int ret;
+
+ if (work -> numb >= work -> bsize)
+ {
+ if (work -> bsize == 0)
+ {
+ work -> bsize = 5;
+ work -> btypevec
+ = (char **) g_malloc (sizeof (char *) * work -> bsize);
+ }
+ else
+ {
+ work -> bsize *= 2;
+ work -> btypevec
+ = (char **) g_realloc ((char *)work -> btypevec,
+ sizeof (char *) * work -> bsize);
+ }
+ }
+ ret = work -> numb++;
+ work -> btypevec[ret] = NULL;
+ return(ret);
+}
+
+/* Store a value into a previously registered B code type. */
+
+static void
+remember_Btype (work, start, len, index)
+ struct work_stuff *work;
+ const char *start;
+ int len, index;
+{
+ char *tem;
+
+ tem = g_malloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ work -> btypevec[index] = tem;
+}
+
+/* Lose all the info related to B and K type codes. */
+static void
+forget_B_and_K_types (work)
+ struct work_stuff *work;
+{
+ int i;
+
+ while (work -> numk > 0)
+ {
+ i = --(work -> numk);
+ if (work -> ktypevec[i] != NULL)
+ {
+ g_free (work -> ktypevec[i]);
+ work -> ktypevec[i] = NULL;
+ }
+ }
+
+ while (work -> numb > 0)
+ {
+ i = --(work -> numb);
+ if (work -> btypevec[i] != NULL)
+ {
+ g_free (work -> btypevec[i]);
+ work -> btypevec[i] = NULL;
+ }
+ }
+}
+/* Forget the remembered types, but not the type vector itself. */
+
+static void
+forget_types (work)
+ struct work_stuff *work;
+{
+ int i;
+
+ while (work -> ntypes > 0)
+ {
+ i = --(work -> ntypes);
+ if (work -> typevec[i] != NULL)
+ {
+ g_free (work -> typevec[i]);
+ work -> typevec[i] = NULL;
+ }
+ }
+}
+
+/* Process the argument list part of the signature, after any class spec
+ has been consumed, as well as the first 'F' character (if any). For
+ example:
+
+ "__als__3fooRT0" => process "RT0"
+ "complexfunc5__FPFPc_PFl_i" => process "PFPc_PFl_i"
+
+ DECLP must be already initialised, usually non-empty. It won't be freed
+ on failure.
+
+ Note that g++ differs significantly from ARM and lucid style mangling
+ with regards to references to previously seen types. For example, given
+ the source fragment:
+
+ class foo {
+ public:
+ foo::foo (int, foo &ia, int, foo &ib, int, foo &ic);
+ };
+
+ foo::foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; }
+ void foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; }
+
+ g++ produces the names:
+
+ __3fooiRT0iT2iT2
+ foo__FiR3fooiT1iT1
+
+ while lcc (and presumably other ARM style compilers as well) produces:
+
+ foo__FiR3fooT1T2T1T2
+ __ct__3fooFiR3fooT1T2T1T2
+
+ Note that g++ bases its type numbers starting at zero and counts all
+ previously seen types, while lucid/ARM bases its type numbers starting
+ at one and only considers types after it has seen the 'F' character
+ indicating the start of the function args. For lucid/ARM style, we
+ account for this difference by discarding any previously seen types when
+ we see the 'F' character, and subtracting one from the type number
+ reference.
+
+ */
+
+static int
+demangle_args (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ string arg;
+ int need_comma = 0;
+ int r;
+ int t;
+ const char *tem;
+ char temptype;
+
+ if (PRINT_ARG_TYPES)
+ {
+ string_append (declp, "(");
+ if (**mangled == '\0')
+ {
+ string_append (declp, "void");
+ }
+ }
+
+ while ((**mangled != '_' && **mangled != '\0' && **mangled != 'e')
+ || work->nrepeats > 0)
+ {
+ if ((**mangled == 'N') || (**mangled == 'T'))
+ {
+ temptype = *(*mangled)++;
+
+ if (temptype == 'N')
+ {
+ if (!get_count (mangled, &r))
+ {
+ return (0);
+ }
+ }
+ else
+ {
+ r = 1;
+ }
+ if ((HP_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING) && work -> ntypes >= 10)
+ {
+ /* If we have 10 or more types we might have more than a 1 digit
+ index so we'll have to consume the whole count here. This
+ will lose if the next thing is a type name preceded by a
+ count but it's impossible to demangle that case properly
+ anyway. Eg if we already have 12 types is T12Pc "(..., type1,
+ Pc, ...)" or "(..., type12, char *, ...)" */
+ if ((t = consume_count(mangled)) <= 0)
+ {
+ return (0);
+ }
+ }
+ else
+ {
+ if (!get_count (mangled, &t))
+ {
+ return (0);
+ }
+ }
+ if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
+ {
+ t--;
+ }
+ /* Validate the type index. Protect against illegal indices from
+ malformed type strings. */
+ if ((t < 0) || (t >= work -> ntypes))
+ {
+ return (0);
+ }
+ while (work->nrepeats > 0 || --r >= 0)
+ {
+ tem = work -> typevec[t];
+ if (need_comma && PRINT_ARG_TYPES)
+ {
+ string_append (declp, ", ");
+ }
+ if (!do_arg (work, &tem, &arg))
+ {
+ return (0);
+ }
+ if (PRINT_ARG_TYPES)
+ {
+ string_appends (declp, &arg);
+ }
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+ else
+ {
+ if (need_comma && PRINT_ARG_TYPES)
+ string_append (declp, ", ");
+ if (!do_arg (work, mangled, &arg))
+ return (0);
+ if (PRINT_ARG_TYPES)
+ string_appends (declp, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+
+ if (**mangled == 'e')
+ {
+ (*mangled)++;
+ if (PRINT_ARG_TYPES)
+ {
+ if (need_comma)
+ {
+ string_append (declp, ",");
+ }
+ string_append (declp, "...");
+ }
+ }
+
+ if (PRINT_ARG_TYPES)
+ {
+ string_append (declp, ")");
+ }
+ return (1);
+}
+
+/* Like demangle_args, but for demangling the argument lists of function
+ and method pointers or references, not top-level declarations. */
+
+static int
+demangle_nested_args (work, mangled, declp)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+{
+ string* saved_previous_argument;
+ int result;
+ int saved_nrepeats;
+
+ /* The G++ name-mangling algorithm does not remember types on nested
+ argument lists, unless -fsquangling is used, and in that case the
+ type vector updated by remember_type is not used. So, we turn
+ off remembering of types here. */
+ ++work->forgetting_types;
+
+ /* For the repeat codes used with -fsquangling, we must keep track of
+ the last argument. */
+ saved_previous_argument = work->previous_argument;
+ saved_nrepeats = work->nrepeats;
+ work->previous_argument = 0;
+ work->nrepeats = 0;
+
+ /* Actually demangle the arguments. */
+ result = demangle_args (work, mangled, declp);
+
+ /* Restore the previous_argument field. */
+ if (work->previous_argument)
+ {
+ string_delete (work->previous_argument);
+ g_free ((char *) work->previous_argument);
+ }
+ work->previous_argument = saved_previous_argument;
+ --work->forgetting_types;
+ work->nrepeats = saved_nrepeats;
+
+ return result;
+}
+
+static void
+demangle_function_name (work, mangled, declp, scan)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+ const char *scan;
+{
+ size_t i;
+ string type;
+ const char *tem;
+
+ string_appendn (declp, (*mangled), scan - (*mangled));
+ string_need (declp, 1);
+ *(declp -> p) = '\0';
+
+ /* Consume the function name, including the "__" separating the name
+ from the signature. We are guaranteed that SCAN points to the
+ separator. */
+
+ (*mangled) = scan + 2;
+ /* We may be looking at an instantiation of a template function:
+ foo__Xt1t2_Ft3t4, where t1, t2, ... are template arguments and a
+ following _F marks the start of the function arguments. Handle
+ the template arguments first. */
+
+ if (HP_DEMANGLING && (**mangled == 'X'))
+ {
+ demangle_arm_hp_template (work, mangled, 0, declp);
+ /* This leaves MANGLED pointing to the 'F' marking func args */
+ }
+
+ if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
+ {
+
+ /* See if we have an ARM style constructor or destructor operator.
+ If so, then just record it, clear the decl, and return.
+ We can't build the actual constructor/destructor decl until later,
+ when we recover the class name from the signature. */
+
+ if (strcmp (declp -> b, "__ct") == 0)
+ {
+ work -> constructor += 1;
+ string_clear (declp);
+ return;
+ }
+ else if (strcmp (declp -> b, "__dt") == 0)
+ {
+ work -> destructor += 1;
+ string_clear (declp);
+ return;
+ }
+ }
+
+ if (declp->p - declp->b >= 3
+ && declp->b[0] == 'o'
+ && declp->b[1] == 'p'
+ && strchr (cplus_markers, declp->b[2]) != NULL)
+ {
+ /* see if it's an assignment expression */
+ if (declp->p - declp->b >= 10 /* op$assign_ */
+ && memcmp (declp->b + 3, "assign_", 7) == 0)
+ {
+ for (i = 0; i < G_N_ELEMENTS (optable); i++)
+ {
+ int len = declp->p - declp->b - 10;
+ if ((int) strlen (optable[i].in) == len
+ && memcmp (optable[i].in, declp->b + 10, len) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ string_append (declp, "=");
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < G_N_ELEMENTS (optable); i++)
+ {
+ int len = declp->p - declp->b - 3;
+ if ((int) strlen (optable[i].in) == len
+ && memcmp (optable[i].in, declp->b + 3, len) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ break;
+ }
+ }
+ }
+ }
+ else if (declp->p - declp->b >= 5 && memcmp (declp->b, "type", 4) == 0
+ && strchr (cplus_markers, declp->b[4]) != NULL)
+ {
+ /* type conversion operator */
+ tem = declp->b + 5;
+ if (do_type (work, &tem, &type))
+ {
+ string_clear (declp);
+ string_append (declp, "operator ");
+ string_appends (declp, &type);
+ string_delete (&type);
+ }
+ }
+ else if (declp->b[0] == '_' && declp->b[1] == '_'
+ && declp->b[2] == 'o' && declp->b[3] == 'p')
+ {
+ /* ANSI. */
+ /* type conversion operator. */
+ tem = declp->b + 4;
+ if (do_type (work, &tem, &type))
+ {
+ string_clear (declp);
+ string_append (declp, "operator ");
+ string_appends (declp, &type);
+ string_delete (&type);
+ }
+ }
+ else if (declp->b[0] == '_' && declp->b[1] == '_'
+ && g_ascii_islower((unsigned char)declp->b[2])
+ && g_ascii_islower((unsigned char)declp->b[3]))
+ {
+ if (declp->b[4] == '\0')
+ {
+ /* Operator. */
+ for (i = 0; i < G_N_ELEMENTS (optable); i++)
+ {
+ if (strlen (optable[i].in) == 2
+ && memcmp (optable[i].in, declp->b + 2, 2) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (declp->b[2] == 'a' && declp->b[5] == '\0')
+ {
+ /* Assignment. */
+ for (i = 0; i < G_N_ELEMENTS (optable); i++)
+ {
+ if (strlen (optable[i].in) == 3
+ && memcmp (optable[i].in, declp->b + 2, 3) == 0)
+ {
+ string_clear (declp);
+ string_append (declp, "operator");
+ string_append (declp, optable[i].out);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* a mini string-handling package */
+
+static void
+string_need (s, n)
+ string *s;
+ int n;
+{
+ int tem;
+
+ if (s->b == NULL)
+ {
+ if (n < 32)
+ {
+ n = 32;
+ }
+ s->p = s->b = g_malloc (n);
+ s->e = s->b + n;
+ }
+ else if (s->e - s->p < n)
+ {
+ tem = s->p - s->b;
+ n += tem;
+ n *= 2;
+ s->b = g_realloc (s->b, n);
+ s->p = s->b + tem;
+ s->e = s->b + n;
+ }
+}
+
+static void
+string_delete (s)
+ string *s;
+{
+ if (s->b != NULL)
+ {
+ g_free (s->b);
+ s->b = s->e = s->p = NULL;
+ }
+}
+
+static void
+string_init (s)
+ string *s;
+{
+ s->b = s->p = s->e = NULL;
+}
+
+static void
+string_clear (s)
+ string *s;
+{
+ s->p = s->b;
+}
+
+#if 0
+
+static int
+string_empty (s)
+ string *s;
+{
+ return (s->b == s->p);
+}
+
+#endif
+
+static void
+string_append (p, s)
+ string *p;
+ const char *s;
+{
+ int n;
+ if (s == NULL || *s == '\0')
+ return;
+ n = strlen (s);
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_appends (p, s)
+ string *p, *s;
+{
+ int n;
+
+ if (s->b != s->p)
+ {
+ n = s->p - s->b;
+ string_need (p, n);
+ memcpy (p->p, s->b, n);
+ p->p += n;
+ }
+}
+
+static void
+string_appendn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ if (n != 0)
+ {
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+ }
+}
+
+static void
+string_prepend (p, s)
+ string *p;
+ const char *s;
+{
+ if (s != NULL && *s != '\0')
+ {
+ string_prependn (p, s, strlen (s));
+ }
+}
+
+static void
+string_prepends (p, s)
+ string *p, *s;
+{
+ if (s->b != s->p)
+ {
+ string_prependn (p, s->b, s->p - s->b);
+ }
+}
+
+static void
+string_prependn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ char *q;
+
+ if (n != 0)
+ {
+ string_need (p, n);
+ for (q = p->p - 1; q >= p->b; q--)
+ {
+ q[n] = q[0];
+ }
+ memcpy (p->b, s, n);
+ p->p += n;
+ }
+}
+
+static void
+string_append_template_idx (s, idx)
+ string *s;
+ int idx;
+{
+ char buf[INTBUF_SIZE + 1 /* 'T' */];
+ sprintf(buf, "T%d", idx);
+ string_append (s, buf);
+}
diff --git a/elfparser.c b/elfparser.c
new file mode 100644
index 0000000..09f08f5
--- /dev/null
+++ b/elfparser.c
@@ -0,0 +1,813 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2006, 2007, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <elf.h>
+#include <sys/mman.h>
+#include "elfparser.h"
+
+typedef struct Section Section;
+
+struct ElfSym
+{
+ gulong table;
+ gulong offset;
+ gulong address;
+};
+
+struct Section
+{
+ const gchar * name;
+ gsize offset;
+ gsize size;
+ gboolean allocated;
+ gulong load_address;
+ guint type;
+};
+
+struct ElfParser
+{
+ gboolean is_64;
+ const guchar * data;
+ gsize length;
+
+ int n_sections;
+ Section ** sections;
+
+ int n_symbols;
+ ElfSym * symbols;
+ gsize sym_strings;
+
+ GMappedFile * file;
+
+ char * filename;
+
+ gboolean checked_build_id;
+ char * build_id;
+
+ const Section * text_section;
+};
+
+/* FIXME: All of these should in principle do endian swapping,
+ * but sysprof never has to deal with binaries of a different
+ * endianness than sysprof itself
+ */
+#define GET_FIELD(parser, offset, struct_name, idx, field_name) \
+ (((parser))->is_64? \
+ ((Elf64_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name : \
+ ((Elf32_ ## struct_name *)(((parser)->data + offset)) + (idx))->field_name)
+
+#define GET_UINT32(parser, offset) \
+ *((uint32_t *)(parser->data + offset)) \
+
+#define GET_SIZE(parser, struct_name) \
+ (((parser)->is_64? \
+ sizeof (Elf64_ ## struct_name) : \
+ sizeof (Elf32_ ## struct_name)))
+
+#define MAKE_ELF_UINT_ACCESSOR(field_name) \
+ static uint64_t field_name (ElfParser *parser) \
+ { \
+ return GET_FIELD (parser, 0, Ehdr, 0, field_name); \
+ }
+
+MAKE_ELF_UINT_ACCESSOR (e_shoff)
+MAKE_ELF_UINT_ACCESSOR (e_shnum)
+MAKE_ELF_UINT_ACCESSOR (e_shstrndx)
+
+#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \
+ static uint64_t field_name (ElfParser *parser, int nth_section) \
+ { \
+ gsize offset = e_shoff (parser); \
+ \
+ return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \
+ }
+
+MAKE_SECTION_HEADER_ACCESSOR (sh_name);
+MAKE_SECTION_HEADER_ACCESSOR (sh_type);
+MAKE_SECTION_HEADER_ACCESSOR (sh_flags);
+MAKE_SECTION_HEADER_ACCESSOR (sh_addr);
+MAKE_SECTION_HEADER_ACCESSOR (sh_offset);
+MAKE_SECTION_HEADER_ACCESSOR (sh_size);
+
+#define MAKE_SYMBOL_ACCESSOR(field_name) \
+ static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \
+ { \
+ return GET_FIELD (parser, offset, Sym, nth, field_name); \
+ }
+
+MAKE_SYMBOL_ACCESSOR(st_name);
+MAKE_SYMBOL_ACCESSOR(st_info);
+MAKE_SYMBOL_ACCESSOR(st_value);
+MAKE_SYMBOL_ACCESSOR(st_size);
+MAKE_SYMBOL_ACCESSOR(st_shndx);
+
+static void
+section_free (Section *section)
+{
+ g_free (section);
+}
+
+static const Section *
+find_section (ElfParser *parser,
+ const char *name,
+ guint type)
+{
+ int i;
+
+ for (i = 0; i < parser->n_sections; ++i)
+ {
+ Section *section = parser->sections[i];
+
+ if (strcmp (section->name, name) == 0 && section->type == type)
+ return section;
+ }
+
+ return NULL;
+}
+
+static gboolean
+parse_elf_signature (const guchar *data,
+ gsize length,
+ gboolean *is_64,
+ gboolean *is_be)
+{
+ /* FIXME: this function should be able to return an error */
+ if (length < EI_NIDENT)
+ {
+ /* FIXME set error */
+ return FALSE;
+ }
+
+ if (data[EI_CLASS] != ELFCLASS32 &&
+ data[EI_CLASS] != ELFCLASS64)
+ {
+ /* FIXME set error */
+ return FALSE;
+ }
+
+ if (data[EI_DATA] != ELFDATA2LSB &&
+ data[EI_DATA] != ELFDATA2MSB)
+ {
+ /* FIXME set error */
+ return FALSE;
+ }
+
+ if (is_64)
+ *is_64 = (data[EI_CLASS] == ELFCLASS64);
+
+ if (is_be)
+ *is_be = (data[EI_DATA] == ELFDATA2MSB);
+
+ return TRUE;
+}
+
+ElfParser *
+elf_parser_new_from_data (const guchar *data,
+ gsize length)
+{
+ ElfParser *parser;
+ gboolean is_64, is_big_endian;
+ int section_names_idx;
+ const guchar *section_names;
+ gsize section_headers;
+ int i;
+
+ if (!parse_elf_signature (data, length, &is_64, &is_big_endian))
+ {
+ /* FIXME: set error */
+ return NULL;
+ }
+
+ parser = g_new0 (ElfParser, 1);
+
+ parser->is_64 = is_64;
+ parser->data = data;
+ parser->length = length;
+
+#if 0
+ g_print (" new parser : %p\n", parser);
+#endif
+
+ /* Read ELF header */
+
+ parser->n_sections = e_shnum (parser);
+ section_names_idx = e_shstrndx (parser);
+ section_headers = e_shoff (parser);
+
+ /* Read section headers */
+ parser->sections = g_new0 (Section *, parser->n_sections);
+
+ section_names = parser->data + sh_offset (parser, section_names_idx);
+
+ for (i = 0; i < parser->n_sections; ++i)
+ {
+ Section *section = g_new (Section, 1);
+
+ section->name = (char *)(section_names + sh_name (parser, i));
+ section->size = sh_size (parser, i);
+ section->offset = sh_offset (parser, i);
+ section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC);
+
+ if (section->allocated)
+ section->load_address = sh_addr (parser, i);
+ else
+ section->load_address = 0;
+
+ section->type = sh_type (parser, i);
+
+ parser->sections[i] = section;
+ }
+
+ /* Cache the text section */
+ parser->text_section = find_section (parser, ".text", SHT_PROGBITS);
+ if (!parser->text_section)
+ parser->text_section = find_section (parser, ".text", SHT_NOBITS);
+
+ parser->filename = NULL;
+ parser->build_id = NULL;
+
+ return parser;
+}
+
+ElfParser *
+elf_parser_new (const char *filename,
+ GError **err)
+{
+ const guchar *data;
+ gsize length;
+ ElfParser *parser;
+
+ GMappedFile *file = g_mapped_file_new (filename, FALSE, NULL);
+
+ if (!file)
+ return NULL;
+
+#if 0
+ g_print ("elf parser new : %s\n", filename);
+#endif
+
+ data = (guchar *)g_mapped_file_get_contents (file);
+ length = g_mapped_file_get_length (file);
+
+#if 0
+ g_print ("data %p: for %s\n", data, filename);
+#endif
+
+ parser = elf_parser_new_from_data (data, length);
+
+#if 0
+ g_print ("Parser for %s: %p\n", filename, parser);
+#endif
+
+ if (!parser)
+ {
+ g_mapped_file_free (file);
+ return NULL;
+ }
+
+ parser->filename = g_strdup (filename);
+
+ parser->file = file;
+
+#if 0
+ g_print ("Elf file: %s (debug: %s)\n",
+ filename, elf_parser_get_debug_link (parser, NULL));
+
+ if (!parser->symbols)
+ g_print ("at this point %s has no symbols\n", filename);
+#endif
+
+ return parser;
+}
+
+guint32
+elf_parser_get_crc32 (ElfParser *parser)
+{
+ static const unsigned long crc32_table[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+ };
+ const guchar *data;
+ gsize length;
+ gulong crc;
+ gsize i;
+
+ data = parser->data;
+ length = parser->length;
+
+ crc = 0xffffffff;
+
+ madvise ((char *)data, length, MADV_SEQUENTIAL);
+
+ for (i = 0; i < length; ++i)
+ crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8);
+
+ /* We just read the entire file into memory, but we only really
+ * need the symbol table, so swap the whole thing out.
+ *
+ * We could be more exact here, but it's only a few minor
+ * pagefaults.
+ */
+ if (parser->file)
+ madvise ((char *)data, length, MADV_DONTNEED);
+
+ return ~crc & 0xffffffff;
+}
+
+void
+elf_parser_free (ElfParser *parser)
+{
+ int i;
+
+ for (i = 0; i < parser->n_sections; ++i)
+ section_free (parser->sections[i]);
+ g_free (parser->sections);
+
+ if (parser->file)
+ g_mapped_file_free (parser->file);
+
+ g_free (parser->symbols);
+
+ if (parser->filename)
+ g_free (parser->filename);
+
+ if (parser->build_id)
+ g_free (parser->build_id);
+
+ g_free (parser);
+}
+
+extern char *sysprof_cplus_demangle (const char *name, int options);
+
+char *
+elf_demangle (const char *name)
+{
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+
+ char *demangled = sysprof_cplus_demangle (name, DMGL_PARAMS | DMGL_ANSI);
+
+ if (demangled)
+ return demangled;
+ else
+ return g_strdup (name);
+}
+
+/*
+ * Looking up symbols
+ */
+static int
+compare_sym (const void *a, const void *b)
+{
+ const ElfSym *sym_a = a;
+ const ElfSym *sym_b = b;
+
+ if (sym_a->address < sym_b->address)
+ return -1;
+ else if (sym_a->address == sym_b->address)
+ return 0;
+ else
+ return 1;
+}
+
+#if 0
+static void
+dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms)
+{
+ int i;
+
+ for (i = 0; i < n_syms; ++i)
+ {
+ ElfSym *s = &(syms[i]);
+
+ g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address);
+ }
+}
+#endif
+
+static void
+read_table (ElfParser *parser,
+ const Section *sym_table,
+ const Section *str_table)
+{
+ int sym_size = GET_SIZE (parser, Sym);
+ int i, n_symbols;
+
+#if 0
+ g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : "<unknown>");
+#endif
+
+ parser->n_symbols = sym_table->size / sym_size;
+ parser->symbols = g_new (ElfSym, parser->n_symbols);
+
+#if 0
+ g_print ("sym table offset: %d\n", sym_table->offset);
+#endif
+
+ n_symbols = 0;
+#if 0
+ g_print ("n syms: %d\n", parser->n_symbols);
+#endif
+ for (i = 0; i < parser->n_symbols; ++i)
+ {
+ guint info;
+ gulong addr;
+ gulong shndx;
+
+ info = st_info (parser, sym_table->offset, i);
+ addr = st_value (parser, sym_table->offset, i);
+ shndx = st_shndx (parser, sym_table->offset, i);
+
+#if 0
+ g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser,
+ parser->sym_format, "st_name",
+ str_table->offset),
+ shndx);
+#endif
+
+ if (addr != 0 &&
+ shndx < parser->n_sections &&
+ parser->sections[shndx] == parser->text_section &&
+ (info & 0xf) == STT_FUNC &&
+ ((info >> 4) == STB_GLOBAL ||
+ (info >> 4) == STB_LOCAL ||
+ (info >> 4) == STB_WEAK))
+ {
+ parser->symbols[n_symbols].address = addr;
+ parser->symbols[n_symbols].table = sym_table->offset;
+ parser->symbols[n_symbols].offset = i;
+
+ n_symbols++;
+
+#if 0
+ g_print (" symbol: %s: %lx\n",
+ get_string_indirect (parser->parser,
+ parser->sym_format, "st_name",
+ str_table->offset),
+ addr - parser->text_section->load_address);
+ g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n",
+ addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
+#endif
+ }
+ else if (addr != 0)
+ {
+#if 0
+ g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n",
+ addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
+#endif
+ }
+ }
+
+ parser->sym_strings = str_table->offset;
+ parser->n_symbols = n_symbols;
+
+ /* Allocate space for at least one symbol, so that parser->symbols will be
+ * non-NULL. If it ends up being NULL, we will be parsing the file over and
+ * over.
+ */
+ parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1);
+
+ qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym);
+}
+
+static void
+read_symbols (ElfParser *parser)
+{
+ const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB);
+ const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB);
+ const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM);
+ const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB);
+
+ if (symtab && strtab)
+ {
+#if 0
+ g_print ("reading symbol table of %s\n", parser->filename);
+#endif
+ read_table (parser, symtab, strtab);
+ }
+ else if (dynsym && dynstr)
+ {
+#if 0
+ g_print ("reading dynamic symbol table of %s\n", parser->filename);
+#endif
+ read_table (parser, dynsym, dynstr);
+ }
+ else
+ {
+ /* To make sure parser->symbols is non-NULL */
+ parser->n_symbols = 0;
+ parser->symbols = g_new (ElfSym, 1);
+ }
+}
+
+static ElfSym *
+do_lookup (ElfSym *symbols,
+ gulong address,
+ int first,
+ int last)
+{
+ if (address >= symbols[last].address)
+ {
+ return &(symbols[last]);
+ }
+ else if (last - first < 3)
+ {
+ while (last >= first)
+ {
+ if (address >= symbols[last].address)
+ return &(symbols[last]);
+
+ last--;
+ }
+
+ return NULL;
+ }
+ else
+ {
+ int mid = (first + last) / 2;
+
+ if (symbols[mid].address > address)
+ return do_lookup (symbols, address, first, mid);
+ else
+ return do_lookup (symbols, address, mid, last);
+ }
+}
+
+/* Address should be given in 'offset into text segment' */
+const ElfSym *
+elf_parser_lookup_symbol (ElfParser *parser,
+ gulong address)
+{
+ const ElfSym *result;
+
+ if (!parser->symbols)
+ {
+#if 0
+ g_print ("reading symbols at %p\n", parser);
+#endif
+ read_symbols (parser);
+ }
+
+ if (parser->n_symbols == 0)
+ return NULL;
+
+ if (!parser->text_section)
+ return NULL;
+
+ address += parser->text_section->load_address;
+
+#if 0
+ g_print ("elf: the address we are looking up is %p\n", address);
+#endif
+
+ result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1);
+
+#if 0
+ if (result)
+ {
+ g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address);
+ }
+ else
+ {
+ g_print ("elf: not found\n");
+ }
+#endif
+
+ if (result)
+ {
+ gulong size = st_size (parser, result->table, result->offset);
+
+ if (size > 0 && result->address + size <= address)
+ {
+#if 0
+ g_print (" elf: ends at %lx, so rejecting\n",
+ result->address + size);
+#endif
+ result = NULL;
+ }
+ }
+
+ if (result)
+ {
+ /* Reject the symbols if the address is outside the text section */
+ if (address > parser->text_section->load_address + parser->text_section->size)
+ result = NULL;
+ }
+
+ return result;
+}
+
+gulong
+elf_parser_get_text_offset (ElfParser *parser)
+{
+ g_return_val_if_fail (parser != NULL, (gulong)-1);
+
+ if (!parser->text_section)
+ return (gulong)-1;
+
+ return parser->text_section->offset;
+}
+
+static gchar *
+make_hex_string (const guchar *data, int n_bytes)
+{
+ static const char hex_digits[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+ GString *string = g_string_new (NULL);
+ int i;
+
+ for (i = 0; i < n_bytes; ++i)
+ {
+ char c = data[i];
+
+ g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]);
+ g_string_append_c (string, hex_digits[(c & 0x0f)]);
+ }
+
+ return g_string_free (string, FALSE);
+}
+
+const gchar *
+elf_parser_get_build_id (ElfParser *parser)
+{
+ if (!parser->checked_build_id)
+ {
+ const Section *build_id =
+ find_section (parser, ".note.gnu.build-id", SHT_NOTE);
+ guint64 name_size;
+ guint64 desc_size;
+ guint64 type;
+ const char *name;
+ guint64 offset;
+
+ parser->checked_build_id = TRUE;
+
+ if (!build_id)
+ return NULL;
+
+ offset = build_id->offset;
+
+ name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz);
+ desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz);
+ type = GET_FIELD (parser, offset, Nhdr, 0, n_type);
+
+ offset += GET_SIZE (parser, Nhdr);
+
+ name = (char *)(parser->data + offset);
+
+ if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID)
+ return NULL;
+
+ offset += strlen (name);
+
+ offset = (offset + 3) & (~0x3);
+
+ parser->build_id = make_hex_string (parser->data + offset, desc_size);
+ }
+
+ return parser->build_id;
+}
+
+const char *
+elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32)
+{
+ guint64 offset;
+ const Section *debug_link = find_section (parser, ".gnu_debuglink",
+ SHT_PROGBITS);
+ const gchar *result;
+
+ if (!debug_link)
+ return NULL;
+
+ offset = debug_link->offset;
+
+ result = (char *)(parser->data + offset);
+
+ if (crc32)
+ {
+ offset = (offset + 3) & ~0x3;
+
+ *crc32 = GET_UINT32 (parser, offset);
+ }
+
+ return result;
+}
+
+const guchar *
+get_section (ElfParser *parser,
+ const char *name)
+{
+ const Section *section = find_section (parser, name, SHT_PROGBITS);
+
+ if (section)
+ return parser->data + section->offset;
+ else
+ return NULL;
+}
+
+const guchar *
+elf_parser_get_eh_frame (ElfParser *parser)
+{
+ return get_section (parser, ".eh_frame");
+}
+
+const guchar *
+elf_parser_get_debug_frame (ElfParser *parser)
+{
+ return get_section (parser, ".debug_frame");
+}
+
+const char *
+elf_parser_get_sym_name (ElfParser *parser,
+ const ElfSym *sym)
+{
+ g_return_val_if_fail (parser != NULL, NULL);
+
+ return (char *)(parser->data + parser->sym_strings +
+ st_name (parser, sym->table, sym->offset));
+}
+
+gboolean
+elf_parser_owns_symbol (ElfParser *parser,
+ const ElfSym *sym)
+{
+ ElfSym *first, *last;
+
+ if (!parser->n_symbols)
+ return FALSE;
+
+ first = parser->symbols;
+ last = parser->symbols + parser->n_symbols - 1;
+
+ return first <= sym && sym <= last;
+}
+
+gulong
+elf_parser_get_sym_address (ElfParser *parser,
+ const ElfSym *sym)
+{
+ return sym->address - parser->text_section->load_address;
+}
+
+/*
+ * Utility functions
+ */
diff --git a/elfparser.h b/elfparser.h
new file mode 100644
index 0000000..c01274c
--- /dev/null
+++ b/elfparser.h
@@ -0,0 +1,57 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2006, 2007, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <glib.h>
+
+typedef struct ElfSym ElfSym;
+typedef struct ElfParser ElfParser;
+
+ElfParser * elf_parser_new_from_data (const guchar *data,
+ gsize length);
+ElfParser * elf_parser_new (const char *filename,
+ GError **err);
+void elf_parser_free (ElfParser *parser);
+const char * elf_parser_get_debug_link (ElfParser *parser,
+ guint32 *crc32);
+const gchar *elf_parser_get_build_id (ElfParser *parser);
+const guchar *elf_parser_get_eh_frame (ElfParser *parser);
+const guchar *elf_parser_get_debug_frame (ElfParser *parser);
+gulong elf_parser_get_text_offset (ElfParser *parser);
+
+
+/* Lookup a symbol in the file.
+ *
+ * The symbol returned is const, so don't free it. It is valid until
+ * elf_parser_free() is called on the parser.
+ *
+ * The address should be given in "file coordinates". This means that
+ * if the file is mapped at address m and offset o, then an address a
+ * should be looked up as "a - (m - o)". (m - o) is where the start
+ * of the file would have been mapped, so a - (m - o) is the position
+ * in the file of a.
+ */
+const ElfSym *elf_parser_lookup_symbol (ElfParser *parser,
+ gulong address);
+guint32 elf_parser_get_crc32 (ElfParser *parser);
+const char * elf_parser_get_sym_name (ElfParser *parser,
+ const ElfSym *sym);
+gulong elf_parser_get_sym_address (ElfParser *parser,
+ const ElfSym *sym);
+gboolean elf_parser_owns_symbol (ElfParser *parser,
+ const ElfSym *sym);
+char * elf_demangle (const char *name);
+
diff --git a/footreedatalist.c b/footreedatalist.c
new file mode 100644
index 0000000..21e4874
--- /dev/null
+++ b/footreedatalist.c
@@ -0,0 +1,577 @@
+/* gtktreedatalist.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * This file contains code shared between GtkTreeStore and GtkListStore. Please
+ * do not use it.
+ */
+
+#include "footreedatalist.h"
+#include <string.h>
+
+static FooTreeDataList *cache;
+
+/* node allocation
+ */
+#define N_DATA_LISTS (64)
+
+FooTreeDataList *
+_foo_tree_data_list_alloc (void)
+{
+ FooTreeDataList *list;
+
+ if (!cache)
+ {
+ int i;
+
+ list = g_malloc (N_DATA_LISTS * sizeof (FooTreeDataList));
+
+ for (i = 0; i < N_DATA_LISTS; ++i)
+ {
+ list[i].next = cache;
+ cache = &(list[i]);
+ }
+ }
+
+ list = cache;
+ cache = cache->next;
+
+ memset (list, 0, sizeof (FooTreeDataList));
+
+ return list;
+}
+
+void
+_foo_tree_data_list_free (FooTreeDataList *list,
+ GType *column_headers)
+{
+ FooTreeDataList *tmp, *next;
+ gint i = 0;
+
+ tmp = list;
+
+ while (tmp)
+ {
+ next = tmp->next;
+ if (g_type_is_a (column_headers [i], G_TYPE_STRING))
+ g_free ((gchar *) tmp->data.v_pointer);
+ else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL)
+ g_object_unref (tmp->data.v_pointer);
+ else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL)
+ g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer);
+
+ tmp->next = cache;
+ cache = tmp;
+
+ i++;
+ tmp = next;
+ }
+}
+
+gboolean
+_foo_tree_data_list_check_type (GType type)
+{
+ gint i = 0;
+ static const GType type_list[] =
+ {
+ G_TYPE_BOOLEAN,
+ G_TYPE_CHAR,
+ G_TYPE_UCHAR,
+ G_TYPE_INT,
+ G_TYPE_UINT,
+ G_TYPE_LONG,
+ G_TYPE_ULONG,
+ G_TYPE_INT64,
+ G_TYPE_UINT64,
+ G_TYPE_ENUM,
+ G_TYPE_FLAGS,
+ G_TYPE_FLOAT,
+ G_TYPE_DOUBLE,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ G_TYPE_BOXED,
+ G_TYPE_OBJECT,
+ G_TYPE_INVALID
+ };
+
+ if (! G_TYPE_IS_VALUE_TYPE (type))
+ return FALSE;
+
+
+ while (type_list[i] != G_TYPE_INVALID)
+ {
+ if (g_type_is_a (type, type_list[i]))
+ return TRUE;
+ i++;
+ }
+ return FALSE;
+}
+
+static inline GType
+get_fundamental_type (GType type)
+{
+ GType result;
+
+ result = G_TYPE_FUNDAMENTAL (type);
+
+ if (result == G_TYPE_INTERFACE)
+ {
+ if (g_type_is_a (type, G_TYPE_OBJECT))
+ result = G_TYPE_OBJECT;
+ }
+
+ return result;
+}
+void
+_foo_tree_data_list_node_to_value (FooTreeDataList *list,
+ GType type,
+ GValue *value)
+{
+ g_value_init (value, type);
+
+ switch (get_fundamental_type (type))
+ {
+ case G_TYPE_BOOLEAN:
+ g_value_set_boolean (value, (gboolean) list->data.v_int);
+ break;
+ case G_TYPE_CHAR:
+ g_value_set_char (value, (gchar) list->data.v_char);
+ break;
+ case G_TYPE_UCHAR:
+ g_value_set_uchar (value, (guchar) list->data.v_uchar);
+ break;
+ case G_TYPE_INT:
+ g_value_set_int (value, (gint) list->data.v_int);
+ break;
+ case G_TYPE_UINT:
+ g_value_set_uint (value, (guint) list->data.v_uint);
+ break;
+ case G_TYPE_LONG:
+ g_value_set_long (value, list->data.v_long);
+ break;
+ case G_TYPE_ULONG:
+ g_value_set_ulong (value, list->data.v_ulong);
+ break;
+ case G_TYPE_INT64:
+ g_value_set_int64 (value, list->data.v_int64);
+ break;
+ case G_TYPE_UINT64:
+ g_value_set_uint64 (value, list->data.v_uint64);
+ break;
+ case G_TYPE_ENUM:
+ g_value_set_enum (value, list->data.v_int);
+ break;
+ case G_TYPE_FLAGS:
+ g_value_set_flags (value, list->data.v_uint);
+ break;
+ case G_TYPE_FLOAT:
+ g_value_set_float (value, (gfloat) list->data.v_float);
+ break;
+ case G_TYPE_DOUBLE:
+ g_value_set_double (value, (gdouble) list->data.v_double);
+ break;
+ case G_TYPE_STRING:
+ g_value_set_string (value, (gchar *) list->data.v_pointer);
+ break;
+ case G_TYPE_POINTER:
+ g_value_set_pointer (value, (gpointer) list->data.v_pointer);
+ break;
+ case G_TYPE_BOXED:
+ g_value_set_boxed (value, (gpointer) list->data.v_pointer);
+ break;
+ case G_TYPE_OBJECT:
+ g_value_set_object (value, (GObject *) list->data.v_pointer);
+ break;
+ default:
+ g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
+ break;
+ }
+}
+
+void
+_foo_tree_data_list_value_to_node (FooTreeDataList *list,
+ GValue *value)
+{
+ switch (get_fundamental_type (G_VALUE_TYPE (value)))
+ {
+ case G_TYPE_BOOLEAN:
+ list->data.v_int = g_value_get_boolean (value);
+ break;
+ case G_TYPE_CHAR:
+ list->data.v_char = g_value_get_char (value);
+ break;
+ case G_TYPE_UCHAR:
+ list->data.v_uchar = g_value_get_uchar (value);
+ break;
+ case G_TYPE_INT:
+ list->data.v_int = g_value_get_int (value);
+ break;
+ case G_TYPE_UINT:
+ list->data.v_uint = g_value_get_uint (value);
+ break;
+ case G_TYPE_LONG:
+ list->data.v_long = g_value_get_long (value);
+ break;
+ case G_TYPE_ULONG:
+ list->data.v_ulong = g_value_get_ulong (value);
+ break;
+ case G_TYPE_INT64:
+ list->data.v_int64 = g_value_get_int64 (value);
+ break;
+ case G_TYPE_UINT64:
+ list->data.v_uint64 = g_value_get_uint64 (value);
+ break;
+ case G_TYPE_ENUM:
+ list->data.v_int = g_value_get_enum (value);
+ break;
+ case G_TYPE_FLAGS:
+ list->data.v_uint = g_value_get_flags (value);
+ break;
+ case G_TYPE_POINTER:
+ list->data.v_pointer = g_value_get_pointer (value);
+ break;
+ case G_TYPE_FLOAT:
+ list->data.v_float = g_value_get_float (value);
+ break;
+ case G_TYPE_DOUBLE:
+ list->data.v_double = g_value_get_double (value);
+ break;
+ case G_TYPE_STRING:
+ g_free (list->data.v_pointer);
+ list->data.v_pointer = g_value_dup_string (value);
+ break;
+ case G_TYPE_OBJECT:
+ if (list->data.v_pointer)
+ g_object_unref (list->data.v_pointer);
+ list->data.v_pointer = g_value_dup_object (value);
+ break;
+ case G_TYPE_BOXED:
+ if (list->data.v_pointer)
+ g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer);
+ list->data.v_pointer = g_value_dup_boxed (value);
+ break;
+ default:
+ g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value)));
+ break;
+ }
+}
+
+FooTreeDataList *
+_foo_tree_data_list_node_copy (FooTreeDataList *list,
+ GType type)
+{
+ FooTreeDataList *new_list;
+
+ g_return_val_if_fail (list != NULL, NULL);
+
+ new_list = _foo_tree_data_list_alloc ();
+ new_list->next = NULL;
+
+ switch (get_fundamental_type (type))
+ {
+ case G_TYPE_BOOLEAN:
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_LONG:
+ case G_TYPE_ULONG:
+ case G_TYPE_INT64:
+ case G_TYPE_UINT64:
+ case G_TYPE_ENUM:
+ case G_TYPE_FLAGS:
+ case G_TYPE_POINTER:
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE:
+ new_list->data = list->data;
+ break;
+ case G_TYPE_STRING:
+ new_list->data.v_pointer = g_strdup (list->data.v_pointer);
+ break;
+ case G_TYPE_OBJECT:
+ case G_TYPE_INTERFACE:
+ new_list->data.v_pointer = list->data.v_pointer;
+ if (new_list->data.v_pointer)
+ g_object_ref (new_list->data.v_pointer);
+ break;
+ case G_TYPE_BOXED:
+ if (list->data.v_pointer)
+ new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer);
+ else
+ new_list->data.v_pointer = NULL;
+ break;
+ default:
+ g_warning ("Unsupported node type (%s) copied.", g_type_name (type));
+ break;
+ }
+
+ return new_list;
+}
+
+gint
+_foo_tree_data_list_compare_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ gint column = GPOINTER_TO_INT (user_data);
+ GType type = gtk_tree_model_get_column_type (model, column);
+ GValue a_value = {0, };
+ GValue b_value = {0, };
+ gint retval;
+ const gchar *stra, *strb;
+
+ gtk_tree_model_get_value (model, a, column, &a_value);
+ gtk_tree_model_get_value (model, b, column, &b_value);
+
+ switch (get_fundamental_type (type))
+ {
+ case G_TYPE_BOOLEAN:
+ if (g_value_get_boolean (&a_value) < g_value_get_boolean (&b_value))
+ retval = -1;
+ else if (g_value_get_boolean (&a_value) == g_value_get_boolean (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_CHAR:
+ if (g_value_get_char (&a_value) < g_value_get_char (&b_value))
+ retval = -1;
+ else if (g_value_get_char (&a_value) == g_value_get_char (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_UCHAR:
+ if (g_value_get_uchar (&a_value) < g_value_get_uchar (&b_value))
+ retval = -1;
+ else if (g_value_get_uchar (&a_value) == g_value_get_uchar (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_INT:
+ if (g_value_get_int (&a_value) < g_value_get_int (&b_value))
+ retval = -1;
+ else if (g_value_get_int (&a_value) == g_value_get_int (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_UINT:
+ if (g_value_get_uint (&a_value) < g_value_get_uint (&b_value))
+ retval = -1;
+ else if (g_value_get_uint (&a_value) == g_value_get_uint (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_LONG:
+ if (g_value_get_long (&a_value) < g_value_get_long (&b_value))
+ retval = -1;
+ else if (g_value_get_long (&a_value) == g_value_get_long (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_ULONG:
+ if (g_value_get_ulong (&a_value) < g_value_get_ulong (&b_value))
+ retval = -1;
+ else if (g_value_get_ulong (&a_value) == g_value_get_ulong (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_INT64:
+ if (g_value_get_int64 (&a_value) < g_value_get_int64 (&b_value))
+ retval = -1;
+ else if (g_value_get_int64 (&a_value) == g_value_get_int64 (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_UINT64:
+ if (g_value_get_uint64 (&a_value) < g_value_get_uint64 (&b_value))
+ retval = -1;
+ else if (g_value_get_uint64 (&a_value) == g_value_get_uint64 (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_ENUM:
+ /* this is somewhat bogus. */
+ if (g_value_get_enum (&a_value) < g_value_get_enum (&b_value))
+ retval = -1;
+ else if (g_value_get_enum (&a_value) == g_value_get_enum (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_FLAGS:
+ /* this is even more bogus. */
+ if (g_value_get_flags (&a_value) < g_value_get_flags (&b_value))
+ retval = -1;
+ else if (g_value_get_flags (&a_value) == g_value_get_flags (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_FLOAT:
+ if (g_value_get_float (&a_value) < g_value_get_float (&b_value))
+ retval = -1;
+ else if (g_value_get_float (&a_value) == g_value_get_float (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_DOUBLE:
+ if (g_value_get_double (&a_value) < g_value_get_double (&b_value))
+ retval = -1;
+ else if (g_value_get_double (&a_value) == g_value_get_double (&b_value))
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ case G_TYPE_STRING:
+ stra = g_value_get_string (&a_value);
+ strb = g_value_get_string (&b_value);
+ if (stra == NULL) stra = "";
+ if (strb == NULL) strb = "";
+ retval = g_utf8_collate (stra, strb);
+ break;
+ case G_TYPE_POINTER:
+ case G_TYPE_BOXED:
+ case G_TYPE_OBJECT:
+ default:
+ g_warning ("Attempting to sort on invalid type %s\n", g_type_name (type));
+ retval = FALSE;
+ break;
+ }
+
+ g_value_unset (&a_value);
+ g_value_unset (&b_value);
+
+ return retval;
+}
+
+
+GList *
+_foo_tree_data_list_header_new (gint n_columns,
+ GType *types)
+{
+ GList *retval = NULL;
+
+ gint i;
+
+ for (i = 0; i < n_columns; i ++)
+ {
+ GtkTreeDataSortHeader *header;
+
+ header = g_slice_new (GtkTreeDataSortHeader);
+
+ retval = g_list_prepend (retval, header);
+ header->sort_column_id = i;
+ header->func = _foo_tree_data_list_compare_func;
+ header->destroy = NULL;
+ header->data = GINT_TO_POINTER (i);
+ }
+ return g_list_reverse (retval);
+}
+
+void
+_foo_tree_data_list_header_free (GList *list)
+{
+ GList *tmp;
+
+ for (tmp = list; tmp; tmp = tmp->next)
+ {
+ GtkTreeDataSortHeader *header = (GtkTreeDataSortHeader *) tmp->data;
+
+ if (header->destroy)
+ {
+ GDestroyNotify d = header->destroy;
+
+ header->destroy = NULL;
+ d (header->data);
+ }
+
+ g_slice_free (GtkTreeDataSortHeader, header);
+ }
+ g_list_free (list);
+}
+
+GtkTreeDataSortHeader *
+_foo_tree_data_list_get_header (GList *header_list,
+ gint sort_column_id)
+{
+ GtkTreeDataSortHeader *header = NULL;
+
+ for (; header_list; header_list = header_list->next)
+ {
+ header = (GtkTreeDataSortHeader*) header_list->data;
+ if (header->sort_column_id == sort_column_id)
+ return header;
+ }
+ return NULL;
+}
+
+
+GList *
+_foo_tree_data_list_set_header (GList *header_list,
+ gint sort_column_id,
+ GtkTreeIterCompareFunc func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ GList *list = header_list;
+ GtkTreeDataSortHeader *header = NULL;
+
+ for (; list; list = list->next)
+ {
+ header = (GtkTreeDataSortHeader*) list->data;
+ if (header->sort_column_id == sort_column_id)
+ break;
+ header = NULL;
+
+ if (list->next == NULL)
+ break;
+ }
+
+ if (header == NULL)
+ {
+ header = g_slice_new0 (GtkTreeDataSortHeader);
+ header->sort_column_id = sort_column_id;
+ if (list)
+ list = g_list_append (list, header);
+ else
+ header_list = g_list_append (header_list, header);
+ }
+
+ if (header->destroy)
+ {
+ GDestroyNotify d = header->destroy;
+
+ header->destroy = NULL;
+ d (header->data);
+ }
+
+ header->func = func;
+ header->data = data;
+ header->destroy = destroy;
+
+ return header_list;
+}
diff --git a/footreedatalist.h b/footreedatalist.h
new file mode 100644
index 0000000..eb4274d
--- /dev/null
+++ b/footreedatalist.h
@@ -0,0 +1,82 @@
+/* gtktreedatalist.h
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TREE_DATA_LIST_H__
+#define __GTK_TREE_DATA_LIST_H__
+
+#include <gtk/gtk.h>
+
+typedef struct _FooTreeDataList FooTreeDataList;
+struct _FooTreeDataList
+{
+ FooTreeDataList *next;
+
+ union {
+ gint v_int;
+ gint8 v_char;
+ guint8 v_uchar;
+ guint v_uint;
+ glong v_long;
+ gulong v_ulong;
+ gint64 v_int64;
+ guint64 v_uint64;
+ gfloat v_float;
+ gdouble v_double;
+ gpointer v_pointer;
+ } data;
+};
+
+typedef struct _GtkTreeDataSortHeader
+{
+ gint sort_column_id;
+ GtkTreeIterCompareFunc func;
+ gpointer data;
+ GDestroyNotify destroy;
+} GtkTreeDataSortHeader;
+
+FooTreeDataList *_foo_tree_data_list_alloc (void);
+void _foo_tree_data_list_free (FooTreeDataList *list,
+ GType *column_headers);
+gboolean _foo_tree_data_list_check_type (GType type);
+void _foo_tree_data_list_node_to_value (FooTreeDataList *list,
+ GType type,
+ GValue *value);
+void _foo_tree_data_list_value_to_node (FooTreeDataList *list,
+ GValue *value);
+
+FooTreeDataList *_foo_tree_data_list_node_copy (FooTreeDataList *list,
+ GType type);
+
+/* Header code */
+gint _foo_tree_data_list_compare_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data);
+GList * _foo_tree_data_list_header_new (gint n_columns,
+ GType *types);
+void _foo_tree_data_list_header_free (GList *header_list);
+GtkTreeDataSortHeader *_foo_tree_data_list_get_header (GList *header_list,
+ gint sort_column_id);
+GList *_foo_tree_data_list_set_header (GList *header_list,
+ gint sort_column_id,
+ GtkTreeIterCompareFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+
+#endif /* __GTK_TREE_DATA_LIST_H__ */
diff --git a/footreestore.c b/footreestore.c
new file mode 100644
index 0000000..e2ab611
--- /dev/null
+++ b/footreestore.c
@@ -0,0 +1,3355 @@
+/* gtktreestore.c
+ * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gobject/gvaluecollector.h>
+#include "footreedatalist.h"
+#include "footreestore.h"
+
+#define G_NODE(node) ((GNode *)node)
+#define FOO_TREE_STORE_IS_SORTED(tree) (((FooTreeStore*)(tree))->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
+#define VALID_ITER(iter, tree_store) ((iter)!= NULL && (iter)->user_data != NULL && ((FooTreeStore*)(tree_store))->stamp == (iter)->stamp)
+
+static void foo_tree_store_tree_model_init (GtkTreeModelIface *iface);
+static void foo_tree_store_drag_source_init(GtkTreeDragSourceIface *iface);
+static void foo_tree_store_drag_dest_init (GtkTreeDragDestIface *iface);
+static void foo_tree_store_sortable_init (GtkTreeSortableIface *iface);
+static void foo_tree_store_buildable_init (GtkBuildableIface *iface);
+static void foo_tree_store_finalize (GObject *object);
+static GtkTreeModelFlags foo_tree_store_get_flags (GtkTreeModel *tree_model);
+static gint foo_tree_store_get_n_columns (GtkTreeModel *tree_model);
+static GType foo_tree_store_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean foo_tree_store_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *foo_tree_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void foo_tree_store_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean foo_tree_store_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean foo_tree_store_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean foo_tree_store_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint foo_tree_store_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean foo_tree_store_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean foo_tree_store_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+
+
+static void foo_tree_store_set_n_columns (FooTreeStore *tree_store,
+ gint n_columns);
+static void foo_tree_store_set_column_type (FooTreeStore *tree_store,
+ gint column,
+ GType type);
+
+static void foo_tree_store_increment_stamp (FooTreeStore *tree_store);
+
+
+/* DND interfaces */
+static gboolean real_foo_tree_store_row_draggable (GtkTreeDragSource *drag_source,
+ GtkTreePath *path);
+static gboolean foo_tree_store_drag_data_delete (GtkTreeDragSource *drag_source,
+ GtkTreePath *path);
+static gboolean foo_tree_store_drag_data_get (GtkTreeDragSource *drag_source,
+ GtkTreePath *path,
+ GtkSelectionData *selection_data);
+static gboolean foo_tree_store_drag_data_received (GtkTreeDragDest *drag_dest,
+ GtkTreePath *dest,
+ GtkSelectionData *selection_data);
+static gboolean foo_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest,
+ GtkTreePath *dest_path,
+ GtkSelectionData *selection_data);
+
+/* Sortable Interfaces */
+
+static void foo_tree_store_sort (FooTreeStore *tree_store);
+static void foo_tree_store_sort_iter_changed (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint column,
+ gboolean emit_signal);
+static gboolean foo_tree_store_get_sort_column_id (GtkTreeSortable *sortable,
+ gint *sort_column_id,
+ GtkSortType *order);
+static void foo_tree_store_set_sort_column_id (GtkTreeSortable *sortable,
+ gint sort_column_id,
+ GtkSortType order);
+static void foo_tree_store_set_sort_func (GtkTreeSortable *sortable,
+ gint sort_column_id,
+ GtkTreeIterCompareFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+static void foo_tree_store_set_default_sort_func (GtkTreeSortable *sortable,
+ GtkTreeIterCompareFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+static gboolean foo_tree_store_has_default_sort_func (GtkTreeSortable *sortable);
+
+
+/* buildable */
+
+static gboolean foo_tree_store_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data);
+static void foo_tree_store_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data);
+
+static void validate_gnode (GNode *node);
+
+static void foo_tree_store_move (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *position,
+ gboolean before);
+
+
+static inline void
+validate_tree (FooTreeStore *tree_store)
+{
+ if (gtk_debug_flags & GTK_DEBUG_TREE)
+ {
+ g_assert (G_NODE (tree_store->root)->parent == NULL);
+
+ validate_gnode (G_NODE (tree_store->root));
+ }
+}
+
+G_DEFINE_TYPE_WITH_CODE (FooTreeStore, foo_tree_store, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+ foo_tree_store_tree_model_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+ foo_tree_store_drag_source_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
+ foo_tree_store_drag_dest_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
+ foo_tree_store_sortable_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+ foo_tree_store_buildable_init))
+
+static void
+foo_tree_store_class_init (FooTreeStoreClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ object_class->finalize = foo_tree_store_finalize;
+}
+
+static void
+foo_tree_store_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = foo_tree_store_get_flags;
+ iface->get_n_columns = foo_tree_store_get_n_columns;
+ iface->get_column_type = foo_tree_store_get_column_type;
+ iface->get_iter = foo_tree_store_get_iter;
+ iface->get_path = foo_tree_store_get_path;
+ iface->get_value = foo_tree_store_get_value;
+ iface->iter_next = foo_tree_store_iter_next;
+ iface->iter_children = foo_tree_store_iter_children;
+ iface->iter_has_child = foo_tree_store_iter_has_child;
+ iface->iter_n_children = foo_tree_store_iter_n_children;
+ iface->iter_nth_child = foo_tree_store_iter_nth_child;
+ iface->iter_parent = foo_tree_store_iter_parent;
+}
+
+static void
+foo_tree_store_drag_source_init (GtkTreeDragSourceIface *iface)
+{
+ iface->row_draggable = real_foo_tree_store_row_draggable;
+ iface->drag_data_delete = foo_tree_store_drag_data_delete;
+ iface->drag_data_get = foo_tree_store_drag_data_get;
+}
+
+static void
+foo_tree_store_drag_dest_init (GtkTreeDragDestIface *iface)
+{
+ iface->drag_data_received = foo_tree_store_drag_data_received;
+ iface->row_drop_possible = foo_tree_store_row_drop_possible;
+}
+
+static void
+foo_tree_store_sortable_init (GtkTreeSortableIface *iface)
+{
+ iface->get_sort_column_id = foo_tree_store_get_sort_column_id;
+ iface->set_sort_column_id = foo_tree_store_set_sort_column_id;
+ iface->set_sort_func = foo_tree_store_set_sort_func;
+ iface->set_default_sort_func = foo_tree_store_set_default_sort_func;
+ iface->has_default_sort_func = foo_tree_store_has_default_sort_func;
+}
+
+void
+foo_tree_store_buildable_init (GtkBuildableIface *iface)
+{
+ iface->custom_tag_start = foo_tree_store_buildable_custom_tag_start;
+ iface->custom_finished = foo_tree_store_buildable_custom_finished;
+}
+
+static void
+foo_tree_store_init (FooTreeStore *tree_store)
+{
+ tree_store->root = g_node_new (NULL);
+ /* While the odds are against us getting 0...
+ */
+ do
+ {
+ tree_store->stamp = g_random_int ();
+ }
+ while (tree_store->stamp == 0);
+
+ tree_store->sort_list = NULL;
+ tree_store->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
+ tree_store->columns_dirty = FALSE;
+
+ tree_store->row_changed_id = g_signal_lookup ("row_changed", GTK_TYPE_TREE_MODEL);
+ tree_store->row_inserted_id = g_signal_lookup ("row_inserted", GTK_TYPE_TREE_MODEL);
+ tree_store->row_has_child_toggled_id = g_signal_lookup ("row_has_child_toggled", GTK_TYPE_TREE_MODEL);
+ tree_store->rows_reordered_id = g_signal_lookup ("rows_reordered", GTK_TYPE_TREE_MODEL);
+}
+
+/**
+ * foo_tree_store_new:
+ * @n_columns: number of columns in the tree store
+ * @Varargs: all #GType types for the columns, from first to last
+ *
+ * Creates a new tree store as with @n_columns columns each of the types passed
+ * in. Note that only types derived from standard GObject fundamental types
+ * are supported.
+ *
+ * As an example, <literal>foo_tree_store_new (3, G_TYPE_INT, G_TYPE_STRING,
+ * GDK_TYPE_PIXBUF);</literal> will create a new #FooTreeStore with three columns, of type
+ * <type>int</type>, <type>string</type> and #GdkPixbuf respectively.
+ *
+ * Return value: a new #FooTreeStore
+ **/
+FooTreeStore *
+foo_tree_store_new (gint n_columns,
+ ...)
+{
+ FooTreeStore *retval;
+ va_list args;
+ gint i;
+
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ retval = g_object_new (FOO_TYPE_TREE_STORE, NULL);
+ foo_tree_store_set_n_columns (retval, n_columns);
+
+ va_start (args, n_columns);
+
+ for (i = 0; i < n_columns; i++)
+ {
+ GType type = va_arg (args, GType);
+ if (! _foo_tree_data_list_check_type (type))
+ {
+ g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
+ g_object_unref (retval);
+ va_end (args);
+ return NULL;
+ }
+ foo_tree_store_set_column_type (retval, i, type);
+ }
+ va_end (args);
+
+ return retval;
+}
+/**
+ * foo_tree_store_newv:
+ * @n_columns: number of columns in the tree store
+ * @types: an array of #GType types for the columns, from first to last
+ *
+ * Non vararg creation function. Used primarily by language bindings.
+ *
+ * Return value: a new #FooTreeStore
+ **/
+FooTreeStore *
+foo_tree_store_newv (gint n_columns,
+ GType *types)
+{
+ FooTreeStore *retval;
+ gint i;
+
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ retval = g_object_new (FOO_TYPE_TREE_STORE, NULL);
+ foo_tree_store_set_n_columns (retval, n_columns);
+
+ for (i = 0; i < n_columns; i++)
+ {
+ if (! _foo_tree_data_list_check_type (types[i]))
+ {
+ g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
+ g_object_unref (retval);
+ return NULL;
+ }
+ foo_tree_store_set_column_type (retval, i, types[i]);
+ }
+
+ return retval;
+}
+
+
+/**
+ * foo_tree_store_set_column_types:
+ * @tree_store: A #FooTreeStore
+ * @n_columns: Number of columns for the tree store
+ * @types: An array of #GType types, one for each column
+ *
+ * This function is meant primarily for #GObjects that inherit from
+ * #FooTreeStore, and should only be used when constructing a new
+ * #FooTreeStore. It will not function after a row has been added,
+ * or a method on the #GtkTreeModel interface is called.
+ **/
+void
+foo_tree_store_set_column_types (FooTreeStore *tree_store,
+ gint n_columns,
+ GType *types)
+{
+ gint i;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (tree_store->columns_dirty == 0);
+
+ foo_tree_store_set_n_columns (tree_store, n_columns);
+ for (i = 0; i < n_columns; i++)
+ {
+ if (! _foo_tree_data_list_check_type (types[i]))
+ {
+ g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (types[i]));
+ continue;
+ }
+ foo_tree_store_set_column_type (tree_store, i, types[i]);
+ }
+}
+
+static void
+foo_tree_store_set_n_columns (FooTreeStore *tree_store,
+ gint n_columns)
+{
+ GType *new_columns;
+
+ if (tree_store->n_columns == n_columns)
+ return;
+
+ new_columns = g_new0 (GType, n_columns);
+ if (tree_store->column_headers)
+ {
+ /* copy the old header orders over */
+ if (n_columns >= tree_store->n_columns)
+ memcpy (new_columns, tree_store->column_headers, tree_store->n_columns * sizeof (gchar *));
+ else
+ memcpy (new_columns, tree_store->column_headers, n_columns * sizeof (GType));
+
+ g_free (tree_store->column_headers);
+ }
+
+ if (tree_store->sort_list)
+ _foo_tree_data_list_header_free (tree_store->sort_list);
+
+ tree_store->sort_list = _foo_tree_data_list_header_new (n_columns, tree_store->column_headers);
+
+ tree_store->column_headers = new_columns;
+ tree_store->n_columns = n_columns;
+}
+
+/**
+ * foo_tree_store_set_column_type:
+ * @tree_store: a #FooTreeStore
+ * @column: column number
+ * @type: type of the data to be stored in @column
+ *
+ * Supported types include: %G_TYPE_UINT, %G_TYPE_INT, %G_TYPE_UCHAR,
+ * %G_TYPE_CHAR, %G_TYPE_BOOLEAN, %G_TYPE_POINTER, %G_TYPE_FLOAT,
+ * %G_TYPE_DOUBLE, %G_TYPE_STRING, %G_TYPE_OBJECT, and %G_TYPE_BOXED, along with
+ * subclasses of those types such as %GDK_TYPE_PIXBUF.
+ *
+ **/
+static void
+foo_tree_store_set_column_type (FooTreeStore *tree_store,
+ gint column,
+ GType type)
+{
+ if (!_foo_tree_data_list_check_type (type))
+ {
+ g_warning ("%s: Invalid type %s\n", G_STRLOC, g_type_name (type));
+ return;
+ }
+ tree_store->column_headers[column] = type;
+}
+
+static gboolean
+node_free (GNode *node, gpointer data)
+{
+ if (node->data)
+ _foo_tree_data_list_free (node->data, (GType*)data);
+ node->data = NULL;
+
+ return FALSE;
+}
+
+static void
+foo_tree_store_finalize (GObject *object)
+{
+ FooTreeStore *tree_store = FOO_TREE_STORE (object);
+
+ g_node_traverse (tree_store->root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
+ node_free, tree_store->column_headers);
+ g_node_destroy (tree_store->root);
+ _foo_tree_data_list_header_free (tree_store->sort_list);
+ g_free (tree_store->column_headers);
+
+ if (tree_store->default_sort_destroy)
+ {
+ GDestroyNotify d = tree_store->default_sort_destroy;
+
+ tree_store->default_sort_destroy = NULL;
+ d (tree_store->default_sort_data);
+ tree_store->default_sort_data = NULL;
+ }
+
+ /* must chain up */
+ G_OBJECT_CLASS (foo_tree_store_parent_class)->finalize (object);
+}
+
+/* fulfill the GtkTreeModel requirements */
+/* NOTE: FooTreeStore::root is a GNode, that acts as the parent node. However,
+ * it is not visible to the tree or to the user., and the path "0" refers to the
+ * first child of FooTreeStore::root.
+ */
+
+
+static GtkTreeModelFlags
+foo_tree_store_get_flags (GtkTreeModel *tree_model)
+{
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint
+foo_tree_store_get_n_columns (GtkTreeModel *tree_model)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+
+ tree_store->columns_dirty = TRUE;
+
+ return tree_store->n_columns;
+}
+
+static GType
+foo_tree_store_get_column_type (GtkTreeModel *tree_model,
+ gint index)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+
+ g_return_val_if_fail (index < tree_store->n_columns, G_TYPE_INVALID);
+
+ tree_store->columns_dirty = TRUE;
+
+ return tree_store->column_headers[index];
+}
+
+static gboolean
+foo_tree_store_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+ GtkTreeIter parent;
+ gint *indices;
+ gint depth, i;
+
+ tree_store->columns_dirty = TRUE;
+
+ indices = gtk_tree_path_get_indices (path);
+ depth = gtk_tree_path_get_depth (path);
+
+ g_return_val_if_fail (depth > 0, FALSE);
+
+ parent.stamp = tree_store->stamp;
+ parent.user_data = tree_store->root;
+
+ if (!foo_tree_store_iter_nth_child (tree_model, iter, &parent, indices[0]))
+ return FALSE;
+
+ for (i = 1; i < depth; i++)
+ {
+ parent = *iter;
+ if (!foo_tree_store_iter_nth_child (tree_model, iter, &parent, indices[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GtkTreePath *
+foo_tree_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+ GtkTreePath *retval;
+ GNode *tmp_node;
+ gint i = 0;
+
+ g_return_val_if_fail (iter->user_data != NULL, NULL);
+ g_return_val_if_fail (iter->stamp == tree_store->stamp, NULL);
+
+ validate_tree (tree_store);
+
+ if (G_NODE (iter->user_data)->parent == NULL &&
+ G_NODE (iter->user_data) == tree_store->root)
+ return gtk_tree_path_new ();
+ g_assert (G_NODE (iter->user_data)->parent != NULL);
+
+ if (G_NODE (iter->user_data)->parent == G_NODE (tree_store->root))
+ {
+ retval = gtk_tree_path_new ();
+ tmp_node = G_NODE (tree_store->root)->children;
+ }
+ else
+ {
+ GtkTreeIter tmp_iter = *iter;
+
+ tmp_iter.user_data = G_NODE (iter->user_data)->parent;
+
+ retval = foo_tree_store_get_path (tree_model, &tmp_iter);
+ tmp_node = G_NODE (iter->user_data)->parent->children;
+ }
+
+ if (retval == NULL)
+ return NULL;
+
+ if (tmp_node == NULL)
+ {
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ for (; tmp_node; tmp_node = tmp_node->next)
+ {
+ if (tmp_node == G_NODE (iter->user_data))
+ break;
+ i++;
+ }
+
+ if (tmp_node == NULL)
+ {
+ /* We couldn't find node, meaning it's prolly not ours */
+ /* Perhaps I should do a g_return_if_fail here. */
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ gtk_tree_path_append_index (retval, i);
+
+ return retval;
+}
+
+
+static void
+foo_tree_store_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+ FooTreeDataList *list;
+ gint tmp_column = column;
+
+ g_return_if_fail (column < tree_store->n_columns);
+ g_return_if_fail (VALID_ITER (iter, tree_store));
+
+ list = G_NODE (iter->user_data)->data;
+
+ while (tmp_column-- > 0 && list)
+ list = list->next;
+
+ if (list)
+ {
+ _foo_tree_data_list_node_to_value (list,
+ tree_store->column_headers[column],
+ value);
+ }
+ else
+ {
+ /* We want to return an initialized but empty (default) value */
+ g_value_init (value, tree_store->column_headers[column]);
+ }
+}
+
+static gboolean
+foo_tree_store_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ g_return_val_if_fail (iter->user_data != NULL, FALSE);
+ g_return_val_if_fail (iter->stamp == FOO_TREE_STORE (tree_model)->stamp, FALSE);
+
+ if (G_NODE (iter->user_data)->next)
+ {
+ iter->user_data = G_NODE (iter->user_data)->next;
+ return TRUE;
+ }
+ else
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+}
+
+static gboolean
+foo_tree_store_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+ GNode *children;
+
+ if (parent)
+ g_return_val_if_fail (VALID_ITER (parent, tree_store), FALSE);
+
+ if (parent)
+ children = G_NODE (parent->user_data)->children;
+ else
+ children = G_NODE (tree_store->root)->children;
+
+ if (children)
+ {
+ iter->stamp = tree_store->stamp;
+ iter->user_data = children;
+ return TRUE;
+ }
+ else
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+}
+
+static gboolean
+foo_tree_store_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ g_return_val_if_fail (iter->user_data != NULL, FALSE);
+ g_return_val_if_fail (VALID_ITER (iter, tree_model), FALSE);
+
+ return G_NODE (iter->user_data)->children != NULL;
+}
+
+static gint
+foo_tree_store_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GNode *node;
+ gint i = 0;
+
+ g_return_val_if_fail (iter == NULL || iter->user_data != NULL, 0);
+
+ if (iter == NULL)
+ node = G_NODE (FOO_TREE_STORE (tree_model)->root)->children;
+ else
+ node = G_NODE (iter->user_data)->children;
+
+ while (node)
+ {
+ i++;
+ node = node->next;
+ }
+
+ return i;
+}
+
+static gboolean
+foo_tree_store_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+ GNode *parent_node;
+ GNode *child;
+
+ g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE);
+
+ if (parent == NULL)
+ parent_node = tree_store->root;
+ else
+ parent_node = parent->user_data;
+
+ child = g_node_nth_child (parent_node, n);
+
+ if (child)
+ {
+ iter->user_data = child;
+ iter->stamp = tree_store->stamp;
+ return TRUE;
+ }
+ else
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+}
+
+static gboolean
+foo_tree_store_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) tree_model;
+ GNode *parent;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (VALID_ITER (child, tree_store), FALSE);
+
+ parent = G_NODE (child->user_data)->parent;
+
+ g_assert (parent != NULL);
+
+ if (parent != tree_store->root)
+ {
+ iter->user_data = parent;
+ iter->stamp = tree_store->stamp;
+ return TRUE;
+ }
+ else
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+}
+
+
+/* Does not emit a signal */
+static gboolean
+foo_tree_store_real_set_value (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value,
+ gboolean sort)
+{
+ FooTreeDataList *list;
+ FooTreeDataList *prev;
+ gint old_column = column;
+ GValue real_value = {0, };
+ gboolean converted = FALSE;
+ gboolean retval = FALSE;
+
+ if (! g_type_is_a (G_VALUE_TYPE (value), tree_store->column_headers[column]))
+ {
+ if (! (g_value_type_compatible (G_VALUE_TYPE (value), tree_store->column_headers[column]) &&
+ g_value_type_compatible (tree_store->column_headers[column], G_VALUE_TYPE (value))))
+ {
+ g_warning ("%s: Unable to convert from %s to %s\n",
+ G_STRLOC,
+ g_type_name (G_VALUE_TYPE (value)),
+ g_type_name (tree_store->column_headers[column]));
+ return retval;
+ }
+ if (!g_value_transform (value, &real_value))
+ {
+ g_warning ("%s: Unable to make conversion from %s to %s\n",
+ G_STRLOC,
+ g_type_name (G_VALUE_TYPE (value)),
+ g_type_name (tree_store->column_headers[column]));
+ g_value_unset (&real_value);
+ return retval;
+ }
+ converted = TRUE;
+ }
+
+ prev = list = G_NODE (iter->user_data)->data;
+
+ while (list != NULL)
+ {
+ if (column == 0)
+ {
+ if (converted)
+ _foo_tree_data_list_value_to_node (list, &real_value);
+ else
+ _foo_tree_data_list_value_to_node (list, value);
+ retval = TRUE;
+ if (converted)
+ g_value_unset (&real_value);
+ if (sort && FOO_TREE_STORE_IS_SORTED (tree_store))
+ foo_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE);
+ return retval;
+ }
+
+ column--;
+ prev = list;
+ list = list->next;
+ }
+
+ if (G_NODE (iter->user_data)->data == NULL)
+ {
+ G_NODE (iter->user_data)->data = list = _foo_tree_data_list_alloc ();
+ list->next = NULL;
+ }
+ else
+ {
+ list = prev->next = _foo_tree_data_list_alloc ();
+ list->next = NULL;
+ }
+
+ while (column != 0)
+ {
+ list->next = _foo_tree_data_list_alloc ();
+ list = list->next;
+ list->next = NULL;
+ column --;
+ }
+
+ if (converted)
+ _foo_tree_data_list_value_to_node (list, &real_value);
+ else
+ _foo_tree_data_list_value_to_node (list, value);
+
+ retval = TRUE;
+ if (converted)
+ g_value_unset (&real_value);
+
+ if (sort && FOO_TREE_STORE_IS_SORTED (tree_store))
+ foo_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE);
+
+ return retval;
+}
+
+/**
+ * foo_tree_store_set_value:
+ * @tree_store: a #FooTreeStore
+ * @iter: A valid #GtkTreeIter for the row being modified
+ * @column: column number to modify
+ * @value: new value for the cell
+ *
+ * Sets the data in the cell specified by @iter and @column.
+ * The type of @value must be convertible to the type of the
+ * column.
+ *
+ **/
+void
+foo_tree_store_set_value (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (VALID_ITER (iter, tree_store));
+ g_return_if_fail (column >= 0 && column < tree_store->n_columns);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ if (foo_tree_store_real_set_value (tree_store, iter, column, value, TRUE))
+ {
+ GtkTreePath *path;
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter);
+ gtk_tree_path_free (path);
+ }
+}
+
+static GtkTreeIterCompareFunc
+foo_tree_store_get_compare_func (FooTreeStore *tree_store)
+{
+ GtkTreeIterCompareFunc func = NULL;
+
+ if (FOO_TREE_STORE_IS_SORTED (tree_store))
+ {
+ if (tree_store->sort_column_id != -1)
+ {
+ GtkTreeDataSortHeader *header;
+ header = _foo_tree_data_list_get_header (tree_store->sort_list,
+ tree_store->sort_column_id);
+ g_return_val_if_fail (header != NULL, NULL);
+ g_return_val_if_fail (header->func != NULL, NULL);
+ func = header->func;
+ }
+ else
+ {
+ func = tree_store->default_sort_func;
+ }
+ }
+
+ return func;
+}
+
+static void
+foo_tree_store_set_vector_internal (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gboolean *emit_signal,
+ gboolean *maybe_need_sort,
+ gint *columns,
+ GValue *values,
+ gint n_values)
+{
+ gint i;
+ GtkTreeIterCompareFunc func = NULL;
+
+ func = foo_tree_store_get_compare_func (tree_store);
+ if (func != _foo_tree_data_list_compare_func)
+ *maybe_need_sort = TRUE;
+
+ for (i = 0; i < n_values; i++)
+ {
+ *emit_signal = foo_tree_store_real_set_value (tree_store, iter,
+ columns[i], &values[i],
+ FALSE) || *emit_signal;
+
+ if (func == _foo_tree_data_list_compare_func &&
+ columns[i] == tree_store->sort_column_id)
+ *maybe_need_sort = TRUE;
+ }
+}
+
+static void
+foo_tree_store_set_valist_internal (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gboolean *emit_signal,
+ gboolean *maybe_need_sort,
+ va_list var_args)
+{
+ gint column;
+ GtkTreeIterCompareFunc func = NULL;
+
+ column = va_arg (var_args, gint);
+
+ func = foo_tree_store_get_compare_func (tree_store);
+ if (func != _foo_tree_data_list_compare_func)
+ *maybe_need_sort = TRUE;
+
+ while (column != -1)
+ {
+ GValue value = { 0, };
+ gchar *error = NULL;
+
+ if (column < 0 || column >= tree_store->n_columns)
+ {
+ g_warning ("%s: Invalid column number %d added to iter (remember to end your list of columns with a -1)", G_STRLOC, column);
+ break;
+ }
+ g_value_init (&value, tree_store->column_headers[column]);
+
+ G_VALUE_COLLECT (&value, var_args, 0, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRLOC, error);
+ g_free (error);
+
+ /* we purposely leak the value here, it might not be
+ * in a sane state if an error condition occoured
+ */
+ break;
+ }
+
+ *emit_signal = foo_tree_store_real_set_value (tree_store,
+ iter,
+ column,
+ &value,
+ FALSE) || *emit_signal;
+
+ if (func == _foo_tree_data_list_compare_func &&
+ column == tree_store->sort_column_id)
+ *maybe_need_sort = TRUE;
+
+ g_value_unset (&value);
+
+ column = va_arg (var_args, gint);
+ }
+}
+
+/**
+ * foo_tree_store_set_valuesv:
+ * @tree_store: A #FooTreeStore
+ * @iter: A valid #GtkTreeIter for the row being modified
+ * @columns: an array of column numbers
+ * @values: an array of GValues
+ * @n_values: the length of the @columns and @values arrays
+ *
+ * A variant of foo_tree_store_set_valist() which takes
+ * the columns and values as two arrays, instead of varargs. This
+ * function is mainly intended for language bindings or in case
+ * the number of columns to change is not known until run-time.
+ *
+ * Since: 2.12
+ **/
+void
+foo_tree_store_set_valuesv (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint *columns,
+ GValue *values,
+ gint n_values)
+{
+ gboolean emit_signal = FALSE;
+ gboolean maybe_need_sort = FALSE;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (VALID_ITER (iter, tree_store));
+
+ foo_tree_store_set_vector_internal (tree_store, iter,
+ &emit_signal,
+ &maybe_need_sort,
+ columns, values, n_values);
+
+ if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store))
+ foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, TRUE);
+
+ if (emit_signal)
+ {
+ GtkTreePath *path;
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter);
+ gtk_tree_path_free (path);
+ }
+}
+
+/**
+ * foo_tree_store_set_valist:
+ * @tree_store: A #FooTreeStore
+ * @iter: A valid #GtkTreeIter for the row being modified
+ * @var_args: <type>va_list</type> of column/value pairs
+ *
+ * See foo_tree_store_set(); this version takes a <type>va_list</type> for
+ * use by language bindings.
+ *
+ **/
+void
+foo_tree_store_set_valist (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ va_list var_args)
+{
+ gboolean emit_signal = FALSE;
+ gboolean maybe_need_sort = FALSE;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (VALID_ITER (iter, tree_store));
+
+ foo_tree_store_set_valist_internal (tree_store, iter,
+ &emit_signal,
+ &maybe_need_sort,
+ var_args);
+
+ if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store))
+ foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, TRUE);
+
+ if (emit_signal)
+ {
+ if (g_signal_has_handler_pending (tree_store, tree_store->row_changed_id, 0, FALSE))
+ {
+ GtkTreePath *path;
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, iter);
+ gtk_tree_path_free (path);
+ }
+ }
+}
+
+/**
+ * foo_tree_store_set:
+ * @tree_store: A #FooTreeStore
+ * @iter: A valid #GtkTreeIter for the row being modified
+ * @Varargs: pairs of column number and value, terminated with -1
+ *
+ * Sets the value of one or more cells in the row referenced by @iter.
+ * The variable argument list should contain integer column numbers,
+ * each column number followed by the value to be set.
+ * The list is terminated by a -1. For example, to set column 0 with type
+ * %G_TYPE_STRING to "Foo", you would write
+ * <literal>foo_tree_store_set (store, iter, 0, "Foo", -1)</literal>.
+ * The value will be copied or referenced by the store if appropriate.
+ **/
+void
+foo_tree_store_set (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ ...)
+{
+ va_list var_args;
+
+ va_start (var_args, iter);
+ foo_tree_store_set_valist (tree_store, iter, var_args);
+ va_end (var_args);
+}
+
+/**
+ * foo_tree_store_remove:
+ * @tree_store: A #FooTreeStore
+ * @iter: A valid #GtkTreeIter
+ *
+ * Removes @iter from @tree_store. After being removed, @iter is set to the
+ * next valid row at that level, or invalidated if it previously pointed to the
+ * last one.
+ *
+ * Return value: %TRUE if @iter is still valid, %FALSE if not.
+ **/
+gboolean
+foo_tree_store_remove (FooTreeStore *tree_store,
+ GtkTreeIter *iter)
+{
+ GtkTreePath *path;
+ GtkTreeIter new_iter = {0,};
+ GNode *parent;
+ GNode *next_node;
+
+ g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), FALSE);
+ g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE);
+
+ parent = G_NODE (iter->user_data)->parent;
+
+ g_assert (parent != NULL);
+ next_node = G_NODE (iter->user_data)->next;
+
+ if (G_NODE (iter->user_data)->data)
+ g_node_traverse (G_NODE (iter->user_data), G_POST_ORDER, G_TRAVERSE_ALL,
+ -1, node_free, tree_store->column_headers);
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ g_node_destroy (G_NODE (iter->user_data));
+
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (tree_store), path);
+
+ if (parent != G_NODE (tree_store->root))
+ {
+ /* child_toggled */
+ if (parent->children == NULL)
+ {
+ gtk_tree_path_up (path);
+
+ new_iter.stamp = tree_store->stamp;
+ new_iter.user_data = parent;
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &new_iter);
+ }
+ }
+ gtk_tree_path_free (path);
+
+ /* revalidate iter */
+ if (next_node != NULL)
+ {
+ iter->stamp = tree_store->stamp;
+ iter->user_data = next_node;
+ return TRUE;
+ }
+ else
+ {
+ iter->stamp = 0;
+ iter->user_data = NULL;
+ }
+
+ return FALSE;
+}
+
+/**
+ * foo_tree_store_insert:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set to the new row
+ * @parent: A valid #GtkTreeIter, or %NULL
+ * @position: position to insert the new row
+ *
+ * Creates a new row at @position. If parent is non-%NULL, then the row will be
+ * made a child of @parent. Otherwise, the row will be created at the toplevel.
+ * If @position is larger than the number of rows at that level, then the new
+ * row will be inserted to the end of the list. @iter will be changed to point
+ * to this new row. The row will be empty after this function is called. To
+ * fill in values, you need to call foo_tree_store_set() or
+ * foo_tree_store_set_value().
+ *
+ **/
+void
+foo_tree_store_insert (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint position)
+{
+ GNode *parent_node;
+ GNode *new_node;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (iter != NULL);
+ if (parent)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+
+ if (parent)
+ parent_node = parent->user_data;
+ else
+ parent_node = tree_store->root;
+
+ tree_store->columns_dirty = TRUE;
+
+ new_node = g_node_new (NULL);
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = new_node;
+ g_node_insert (parent_node, position, new_node);
+
+ if (g_signal_has_handler_pending (tree_store, tree_store->row_inserted_id, 0, FALSE) ||
+ g_signal_has_handler_pending (tree_store, tree_store->row_has_child_toggled_id, 0, FALSE))
+ {
+ GtkTreePath *path;
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ if (new_node->prev == NULL && new_node->next == NULL)
+ {
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent);
+ }
+ }
+
+ gtk_tree_path_free (path);
+ }
+
+ validate_tree ((FooTreeStore*)tree_store);
+}
+
+/**
+ * foo_tree_store_insert_before:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set to the new row
+ * @parent: A valid #GtkTreeIter, or %NULL
+ * @sibling: A valid #GtkTreeIter, or %NULL
+ *
+ * Inserts a new row before @sibling. If @sibling is %NULL, then the row will
+ * be appended to @parent 's children. If @parent and @sibling are %NULL, then
+ * the row will be appended to the toplevel. If both @sibling and @parent are
+ * set, then @parent must be the parent of @sibling. When @sibling is set,
+ * @parent is optional.
+ *
+ * @iter will be changed to point to this new row. The row will be empty after
+ * this function is called. To fill in values, you need to call
+ * foo_tree_store_set() or foo_tree_store_set_value().
+ *
+ **/
+void
+foo_tree_store_insert_before (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ GtkTreeIter *sibling)
+{
+ GtkTreePath *path;
+ GNode *parent_node = NULL;
+ GNode *new_node;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (iter != NULL);
+ if (parent != NULL)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+ if (sibling != NULL)
+ g_return_if_fail (VALID_ITER (sibling, tree_store));
+
+ if (parent == NULL && sibling == NULL)
+ parent_node = tree_store->root;
+ else if (parent == NULL)
+ parent_node = G_NODE (sibling->user_data)->parent;
+ else if (sibling == NULL)
+ parent_node = G_NODE (parent->user_data);
+ else
+ {
+ g_return_if_fail (G_NODE (sibling->user_data)->parent == G_NODE (parent->user_data));
+ parent_node = G_NODE (parent->user_data);
+ }
+
+ tree_store->columns_dirty = TRUE;
+
+ new_node = g_node_new (NULL);
+
+ g_node_insert_before (parent_node,
+ sibling ? G_NODE (sibling->user_data) : NULL,
+ new_node);
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = new_node;
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ if (new_node->prev == NULL && new_node->next == NULL)
+ {
+ GtkTreeIter parent_iter;
+
+ parent_iter.stamp = tree_store->stamp;
+ parent_iter.user_data = parent_node;
+
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter);
+ }
+ }
+
+ gtk_tree_path_free (path);
+
+ validate_tree (tree_store);
+}
+
+/**
+ * foo_tree_store_insert_after:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set to the new row
+ * @parent: A valid #GtkTreeIter, or %NULL
+ * @sibling: A valid #GtkTreeIter, or %NULL
+ *
+ * Inserts a new row after @sibling. If @sibling is %NULL, then the row will be
+ * prepended to @parent 's children. If @parent and @sibling are %NULL, then
+ * the row will be prepended to the toplevel. If both @sibling and @parent are
+ * set, then @parent must be the parent of @sibling. When @sibling is set,
+ * @parent is optional.
+ *
+ * @iter will be changed to point to this new row. The row will be empty after
+ * this function is called. To fill in values, you need to call
+ * foo_tree_store_set() or foo_tree_store_set_value().
+ *
+ **/
+void
+foo_tree_store_insert_after (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ GtkTreeIter *sibling)
+{
+ GtkTreePath *path;
+ GNode *parent_node;
+ GNode *new_node;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (iter != NULL);
+ if (parent != NULL)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+ if (sibling != NULL)
+ g_return_if_fail (VALID_ITER (sibling, tree_store));
+
+ if (parent == NULL && sibling == NULL)
+ parent_node = tree_store->root;
+ else if (parent == NULL)
+ parent_node = G_NODE (sibling->user_data)->parent;
+ else if (sibling == NULL)
+ parent_node = G_NODE (parent->user_data);
+ else
+ {
+ g_return_if_fail (G_NODE (sibling->user_data)->parent ==
+ G_NODE (parent->user_data));
+ parent_node = G_NODE (parent->user_data);
+ }
+
+ tree_store->columns_dirty = TRUE;
+
+ new_node = g_node_new (NULL);
+
+ g_node_insert_after (parent_node,
+ sibling ? G_NODE (sibling->user_data) : NULL,
+ new_node);
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = new_node;
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ if (new_node->prev == NULL && new_node->next == NULL)
+ {
+ GtkTreeIter parent_iter;
+
+ parent_iter.stamp = tree_store->stamp;
+ parent_iter.user_data = parent_node;
+
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, &parent_iter);
+ }
+ }
+
+ gtk_tree_path_free (path);
+
+ validate_tree (tree_store);
+}
+
+/**
+ * foo_tree_store_insert_with_values:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set the new row, or %NULL.
+ * @parent: A valid #GtkTreeIter, or %NULL
+ * @position: position to insert the new row
+ * @Varargs: pairs of column number and value, terminated with -1
+ *
+ * Creates a new row at @position. @iter will be changed to point to this
+ * new row. If @position is larger than the number of rows on the list, then
+ * the new row will be appended to the list. The row will be filled with
+ * the values given to this function.
+ *
+ * Calling
+ * <literal>foo_tree_store_insert_with_values (tree_store, iter, position, ...)</literal>
+ * has the same effect as calling
+ * |[
+ * foo_tree_store_insert (tree_store, iter, position);
+ * foo_tree_store_set (tree_store, iter, ...);
+ * ]|
+ * with the different that the former will only emit a row_inserted signal,
+ * while the latter will emit row_inserted, row_changed and if the tree store
+ * is sorted, rows_reordered. Since emitting the rows_reordered signal
+ * repeatedly can affect the performance of the program,
+ * foo_tree_store_insert_with_values() should generally be preferred when
+ * inserting rows in a sorted tree store.
+ *
+ * Since: 2.10
+ */
+void
+foo_tree_store_insert_with_values (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint position,
+ ...)
+{
+ GtkTreePath *path;
+ GNode *parent_node;
+ GNode *new_node;
+ GtkTreeIter tmp_iter;
+ va_list var_args;
+ gboolean changed = FALSE;
+ gboolean maybe_need_sort = FALSE;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+
+ if (!iter)
+ iter = &tmp_iter;
+
+ if (parent)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+
+ if (parent)
+ parent_node = parent->user_data;
+ else
+ parent_node = tree_store->root;
+
+ tree_store->columns_dirty = TRUE;
+
+ new_node = g_node_new (NULL);
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = new_node;
+ g_node_insert (parent_node, position, new_node);
+
+ va_start (var_args, position);
+ foo_tree_store_set_valist_internal (tree_store, iter,
+ &changed, &maybe_need_sort,
+ var_args);
+ va_end (var_args);
+
+ if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store))
+ foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, FALSE);
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ if (new_node->prev == NULL && new_node->next == NULL)
+ {
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent);
+ }
+ }
+
+ gtk_tree_path_free (path);
+
+ validate_tree ((FooTreeStore *)tree_store);
+}
+
+/**
+ * foo_tree_store_insert_with_valuesv:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set the new row, or %NULL.
+ * @parent: A valid #GtkTreeIter, or %NULL
+ * @position: position to insert the new row
+ * @columns: an array of column numbers
+ * @values: an array of GValues
+ * @n_values: the length of the @columns and @values arrays
+ *
+ * A variant of foo_tree_store_insert_with_values() which takes
+ * the columns and values as two arrays, instead of varargs. This
+ * function is mainly intended for language bindings.
+ *
+ * Since: 2.10
+ */
+void
+foo_tree_store_insert_with_valuesv (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint position,
+ gint *columns,
+ GValue *values,
+ gint n_values)
+{
+ GtkTreePath *path;
+ GNode *parent_node;
+ GNode *new_node;
+ GtkTreeIter tmp_iter;
+ gboolean changed = FALSE;
+ gboolean maybe_need_sort = FALSE;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+
+ if (!iter)
+ iter = &tmp_iter;
+
+ if (parent)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+
+ if (parent)
+ parent_node = parent->user_data;
+ else
+ parent_node = tree_store->root;
+
+ tree_store->columns_dirty = TRUE;
+
+ new_node = g_node_new (NULL);
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = new_node;
+ g_node_insert (parent_node, position, new_node);
+
+ foo_tree_store_set_vector_internal (tree_store, iter,
+ &changed, &maybe_need_sort,
+ columns, values, n_values);
+
+ if (maybe_need_sort && FOO_TREE_STORE_IS_SORTED (tree_store))
+ foo_tree_store_sort_iter_changed (tree_store, iter, tree_store->sort_column_id, FALSE);
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ if (new_node->prev == NULL && new_node->next == NULL)
+ {
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent);
+ }
+ }
+
+ gtk_tree_path_free (path);
+
+ validate_tree ((FooTreeStore *)tree_store);
+}
+
+/**
+ * foo_tree_store_prepend:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set to the prepended row
+ * @parent: A valid #GtkTreeIter, or %NULL
+ *
+ * Prepends a new row to @tree_store. If @parent is non-%NULL, then it will prepend
+ * the new row before the first child of @parent, otherwise it will prepend a row
+ * to the top level. @iter will be changed to point to this new row. The row
+ * will be empty after this function is called. To fill in values, you need to
+ * call foo_tree_store_set() or foo_tree_store_set_value().
+ **/
+void
+foo_tree_store_prepend (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ GNode *parent_node;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (iter != NULL);
+ if (parent != NULL)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+
+ tree_store->columns_dirty = TRUE;
+
+ if (parent == NULL)
+ parent_node = tree_store->root;
+ else
+ parent_node = parent->user_data;
+
+ if (parent_node->children == NULL)
+ {
+ GtkTreePath *path;
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = g_node_new (NULL);
+
+ g_node_prepend (parent_node, G_NODE (iter->user_data));
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent);
+ }
+ gtk_tree_path_free (path);
+ }
+ else
+ {
+ foo_tree_store_insert_after (tree_store, iter, parent, NULL);
+ }
+
+ validate_tree (tree_store);
+}
+
+/**
+ * foo_tree_store_append:
+ * @tree_store: A #FooTreeStore
+ * @iter: An unset #GtkTreeIter to set to the appended row
+ * @parent: A valid #GtkTreeIter, or %NULL
+ *
+ * Appends a new row to @tree_store. If @parent is non-%NULL, then it will append the
+ * new row after the last child of @parent, otherwise it will append a row to
+ * the top level. @iter will be changed to point to this new row. The row will
+ * be empty after this function is called. To fill in values, you need to call
+ * foo_tree_store_set() or foo_tree_store_set_value().
+ **/
+void
+foo_tree_store_append (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ GNode *parent_node;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (iter != NULL);
+ if (parent != NULL)
+ g_return_if_fail (VALID_ITER (parent, tree_store));
+
+ if (parent == NULL)
+ parent_node = tree_store->root;
+ else
+ parent_node = parent->user_data;
+
+ tree_store->columns_dirty = TRUE;
+
+ if (parent_node->children == NULL)
+ {
+ GtkTreePath *path;
+
+ iter->stamp = tree_store->stamp;
+ iter->user_data = g_node_new (NULL);
+
+ g_node_append (parent_node, G_NODE (iter->user_data));
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (tree_store), path, iter);
+
+ if (parent_node != tree_store->root)
+ {
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (tree_store), path, parent);
+ }
+ gtk_tree_path_free (path);
+ }
+ else
+ {
+ foo_tree_store_insert_before (tree_store, iter, parent, NULL);
+ }
+
+ validate_tree (tree_store);
+}
+
+/**
+ * foo_tree_store_is_ancestor:
+ * @tree_store: A #FooTreeStore
+ * @iter: A valid #GtkTreeIter
+ * @descendant: A valid #GtkTreeIter
+ *
+ * Returns %TRUE if @iter is an ancestor of @descendant. That is, @iter is the
+ * parent (or grandparent or great-grandparent) of @descendant.
+ *
+ * Return value: %TRUE, if @iter is an ancestor of @descendant
+ **/
+gboolean
+foo_tree_store_is_ancestor (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *descendant)
+{
+ g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), FALSE);
+ g_return_val_if_fail (VALID_ITER (iter, tree_store), FALSE);
+ g_return_val_if_fail (VALID_ITER (descendant, tree_store), FALSE);
+
+ return g_node_is_ancestor (G_NODE (iter->user_data),
+ G_NODE (descendant->user_data));
+}
+
+
+/**
+ * foo_tree_store_iter_depth:
+ * @tree_store: A #FooTreeStore
+ * @iter: A valid #GtkTreeIter
+ *
+ * Returns the depth of @iter. This will be 0 for anything on the root level, 1
+ * for anything down a level, etc.
+ *
+ * Return value: The depth of @iter
+ **/
+gint
+foo_tree_store_iter_depth (FooTreeStore *tree_store,
+ GtkTreeIter *iter)
+{
+ g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), 0);
+ g_return_val_if_fail (VALID_ITER (iter, tree_store), 0);
+
+ return g_node_depth (G_NODE (iter->user_data)) - 2;
+}
+
+/* simple ripoff from g_node_traverse_post_order */
+static gboolean
+foo_tree_store_clear_traverse (GNode *node,
+ FooTreeStore *store)
+{
+ GtkTreeIter iter;
+
+ if (node->children)
+ {
+ GNode *child;
+
+ child = node->children;
+ while (child)
+ {
+ register GNode *current;
+
+ current = child;
+ child = current->next;
+ if (foo_tree_store_clear_traverse (current, store))
+ return TRUE;
+ }
+
+ if (node->parent)
+ {
+ iter.stamp = store->stamp;
+ iter.user_data = node;
+
+ foo_tree_store_remove (store, &iter);
+ }
+ }
+ else if (node->parent)
+ {
+ iter.stamp = store->stamp;
+ iter.user_data = node;
+
+ foo_tree_store_remove (store, &iter);
+ }
+
+ return FALSE;
+}
+
+static void
+foo_tree_store_increment_stamp (FooTreeStore *tree_store)
+{
+ do
+ {
+ tree_store->stamp++;
+ }
+ while (tree_store->stamp == 0);
+}
+
+/**
+ * foo_tree_store_clear:
+ * @tree_store: a #FooTreeStore
+ *
+ * Removes all rows from @tree_store
+ **/
+void
+foo_tree_store_clear (FooTreeStore *tree_store)
+{
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+
+ foo_tree_store_clear_traverse (tree_store->root, tree_store);
+ foo_tree_store_increment_stamp (tree_store);
+}
+
+static gboolean
+foo_tree_store_iter_is_valid_helper (GtkTreeIter *iter,
+ GNode *first)
+{
+ GNode *node;
+
+ node = first;
+
+ do
+ {
+ if (node == iter->user_data)
+ return TRUE;
+
+ if (node->children)
+ if (foo_tree_store_iter_is_valid_helper (iter, node->children))
+ return TRUE;
+
+ node = node->next;
+ }
+ while (node);
+
+ return FALSE;
+}
+
+/**
+ * foo_tree_store_iter_is_valid:
+ * @tree_store: A #FooTreeStore.
+ * @iter: A #GtkTreeIter.
+ *
+ * WARNING: This function is slow. Only use it for debugging and/or testing
+ * purposes.
+ *
+ * Checks if the given iter is a valid iter for this #FooTreeStore.
+ *
+ * Return value: %TRUE if the iter is valid, %FALSE if the iter is invalid.
+ *
+ * Since: 2.2
+ **/
+gboolean
+foo_tree_store_iter_is_valid (FooTreeStore *tree_store,
+ GtkTreeIter *iter)
+{
+ g_return_val_if_fail (FOO_IS_TREE_STORE (tree_store), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ if (!VALID_ITER (iter, tree_store))
+ return FALSE;
+
+ return foo_tree_store_iter_is_valid_helper (iter, tree_store->root);
+}
+
+/* DND */
+
+
+static gboolean real_foo_tree_store_row_draggable (GtkTreeDragSource *drag_source,
+ GtkTreePath *path)
+{
+ return TRUE;
+}
+
+static gboolean
+foo_tree_store_drag_data_delete (GtkTreeDragSource *drag_source,
+ GtkTreePath *path)
+{
+ GtkTreeIter iter;
+
+ if (foo_tree_store_get_iter (GTK_TREE_MODEL (drag_source),
+ &iter,
+ path))
+ {
+ foo_tree_store_remove (FOO_TREE_STORE (drag_source),
+ &iter);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static gboolean
+foo_tree_store_drag_data_get (GtkTreeDragSource *drag_source,
+ GtkTreePath *path,
+ GtkSelectionData *selection_data)
+{
+ /* Note that we don't need to handle the GTK_TREE_MODEL_ROW
+ * target, because the default handler does it for us, but
+ * we do anyway for the convenience of someone maybe overriding the
+ * default handler.
+ */
+
+ if (gtk_tree_set_row_drag_data (selection_data,
+ GTK_TREE_MODEL (drag_source),
+ path))
+ {
+ return TRUE;
+ }
+ else
+ {
+ /* FIXME handle text targets at least. */
+ }
+
+ return FALSE;
+}
+
+static void
+copy_node_data (FooTreeStore *tree_store,
+ GtkTreeIter *src_iter,
+ GtkTreeIter *dest_iter)
+{
+ FooTreeDataList *dl = G_NODE (src_iter->user_data)->data;
+ FooTreeDataList *copy_head = NULL;
+ FooTreeDataList *copy_prev = NULL;
+ FooTreeDataList *copy_iter = NULL;
+ GtkTreePath *path;
+ gint col;
+
+ col = 0;
+ while (dl)
+ {
+ copy_iter = _foo_tree_data_list_node_copy (dl, tree_store->column_headers[col]);
+
+ if (copy_head == NULL)
+ copy_head = copy_iter;
+
+ if (copy_prev)
+ copy_prev->next = copy_iter;
+
+ copy_prev = copy_iter;
+
+ dl = dl->next;
+ ++col;
+ }
+
+ G_NODE (dest_iter->user_data)->data = copy_head;
+
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), dest_iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, dest_iter);
+ gtk_tree_path_free (path);
+}
+
+static void
+recursive_node_copy (FooTreeStore *tree_store,
+ GtkTreeIter *src_iter,
+ GtkTreeIter *dest_iter)
+{
+ GtkTreeIter child;
+ GtkTreeModel *model;
+
+ model = GTK_TREE_MODEL (tree_store);
+
+ copy_node_data (tree_store, src_iter, dest_iter);
+
+ if (foo_tree_store_iter_children (model,
+ &child,
+ src_iter))
+ {
+ /* Need to create children and recurse. Note our
+ * dependence on persistent iterators here.
+ */
+ do
+ {
+ GtkTreeIter copy;
+
+ /* Gee, a really slow algorithm... ;-) FIXME */
+ foo_tree_store_append (tree_store,
+ &copy,
+ dest_iter);
+
+ recursive_node_copy (tree_store, &child, &copy);
+ }
+ while (foo_tree_store_iter_next (model, &child));
+ }
+}
+
+static gboolean
+foo_tree_store_drag_data_received (GtkTreeDragDest *drag_dest,
+ GtkTreePath *dest,
+ GtkSelectionData *selection_data)
+{
+ GtkTreeModel *tree_model;
+ FooTreeStore *tree_store;
+ GtkTreeModel *src_model = NULL;
+ GtkTreePath *src_path = NULL;
+ gboolean retval = FALSE;
+
+ tree_model = GTK_TREE_MODEL (drag_dest);
+ tree_store = FOO_TREE_STORE (drag_dest);
+
+ validate_tree (tree_store);
+
+ if (gtk_tree_get_row_drag_data (selection_data,
+ &src_model,
+ &src_path) &&
+ src_model == tree_model)
+ {
+ /* Copy the given row to a new position */
+ GtkTreeIter src_iter;
+ GtkTreeIter dest_iter;
+ GtkTreePath *prev;
+
+ if (!foo_tree_store_get_iter (src_model,
+ &src_iter,
+ src_path))
+ {
+ goto out;
+ }
+
+ /* Get the path to insert _after_ (dest is the path to insert _before_) */
+ prev = gtk_tree_path_copy (dest);
+
+ if (!gtk_tree_path_prev (prev))
+ {
+ GtkTreeIter dest_parent;
+ GtkTreePath *parent;
+ GtkTreeIter *dest_parent_p;
+
+ /* dest was the first spot at the current depth; which means
+ * we are supposed to prepend.
+ */
+
+ /* Get the parent, NULL if parent is the root */
+ dest_parent_p = NULL;
+ parent = gtk_tree_path_copy (dest);
+ if (gtk_tree_path_up (parent) &&
+ gtk_tree_path_get_depth (parent) > 0)
+ {
+ foo_tree_store_get_iter (tree_model,
+ &dest_parent,
+ parent);
+ dest_parent_p = &dest_parent;
+ }
+ gtk_tree_path_free (parent);
+ parent = NULL;
+
+ foo_tree_store_prepend (tree_store,
+ &dest_iter,
+ dest_parent_p);
+
+ retval = TRUE;
+ }
+ else
+ {
+ if (foo_tree_store_get_iter (tree_model, &dest_iter, prev))
+ {
+ GtkTreeIter tmp_iter = dest_iter;
+
+ foo_tree_store_insert_after (tree_store, &dest_iter, NULL,
+ &tmp_iter);
+
+ retval = TRUE;
+ }
+ }
+
+ gtk_tree_path_free (prev);
+
+ /* If we succeeded in creating dest_iter, walk src_iter tree branch,
+ * duplicating it below dest_iter.
+ */
+
+ if (retval)
+ {
+ recursive_node_copy (tree_store,
+ &src_iter,
+ &dest_iter);
+ }
+ }
+ else
+ {
+ /* FIXME maybe add some data targets eventually, or handle text
+ * targets in the simple case.
+ */
+
+ }
+
+ out:
+
+ if (src_path)
+ gtk_tree_path_free (src_path);
+
+ return retval;
+}
+
+static gboolean
+foo_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest,
+ GtkTreePath *dest_path,
+ GtkSelectionData *selection_data)
+{
+ GtkTreeModel *src_model = NULL;
+ GtkTreePath *src_path = NULL;
+ GtkTreePath *tmp = NULL;
+ gboolean retval = FALSE;
+
+ /* don't accept drops if the tree has been sorted */
+ if (FOO_TREE_STORE_IS_SORTED (drag_dest))
+ return FALSE;
+
+ if (!gtk_tree_get_row_drag_data (selection_data,
+ &src_model,
+ &src_path))
+ goto out;
+
+ /* can only drag to ourselves */
+ if (src_model != GTK_TREE_MODEL (drag_dest))
+ goto out;
+
+ /* Can't drop into ourself. */
+ if (gtk_tree_path_is_ancestor (src_path,
+ dest_path))
+ goto out;
+
+ /* Can't drop if dest_path's parent doesn't exist */
+ {
+ GtkTreeIter iter;
+
+ if (gtk_tree_path_get_depth (dest_path) > 1)
+ {
+ tmp = gtk_tree_path_copy (dest_path);
+ gtk_tree_path_up (tmp);
+
+ if (!foo_tree_store_get_iter (GTK_TREE_MODEL (drag_dest),
+ &iter, tmp))
+ goto out;
+ }
+ }
+
+ /* Can otherwise drop anywhere. */
+ retval = TRUE;
+
+ out:
+
+ if (src_path)
+ gtk_tree_path_free (src_path);
+ if (tmp)
+ gtk_tree_path_free (tmp);
+
+ return retval;
+}
+
+/* Sorting and reordering */
+typedef struct _SortTuple
+{
+ gint offset;
+ GNode *node;
+} SortTuple;
+
+/* Reordering */
+static gint
+foo_tree_store_reorder_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ SortTuple *a_reorder;
+ SortTuple *b_reorder;
+
+ a_reorder = (SortTuple *)a;
+ b_reorder = (SortTuple *)b;
+
+ if (a_reorder->offset < b_reorder->offset)
+ return -1;
+ if (a_reorder->offset > b_reorder->offset)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * foo_tree_store_reorder:
+ * @tree_store: A #FooTreeStore.
+ * @parent: A #GtkTreeIter.
+ * @new_order: an array of integers mapping the new position of each child
+ * to its old position before the re-ordering,
+ * i.e. @new_order<literal>[newpos] = oldpos</literal>.
+ *
+ * Reorders the children of @parent in @tree_store to follow the order
+ * indicated by @new_order. Note that this function only works with
+ * unsorted stores.
+ *
+ * Since: 2.2
+ **/
+void
+foo_tree_store_reorder (FooTreeStore *tree_store,
+ GtkTreeIter *parent,
+ gint *new_order)
+{
+ gint i, length = 0;
+ GNode *level, *node;
+ GtkTreePath *path;
+ SortTuple *sort_array;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (!FOO_TREE_STORE_IS_SORTED (tree_store));
+ g_return_if_fail (parent == NULL || VALID_ITER (parent, tree_store));
+ g_return_if_fail (new_order != NULL);
+
+ if (!parent)
+ level = G_NODE (tree_store->root)->children;
+ else
+ level = G_NODE (parent->user_data)->children;
+
+ /* count nodes */
+ node = level;
+ while (node)
+ {
+ length++;
+ node = node->next;
+ }
+
+ /* set up sortarray */
+ sort_array = g_new (SortTuple, length);
+
+ node = level;
+ for (i = 0; i < length; i++)
+ {
+ sort_array[new_order[i]].offset = i;
+ sort_array[i].node = node;
+
+ node = node->next;
+ }
+
+ g_qsort_with_data (sort_array,
+ length,
+ sizeof (SortTuple),
+ foo_tree_store_reorder_func,
+ NULL);
+
+ /* fix up level */
+ for (i = 0; i < length - 1; i++)
+ {
+ sort_array[i].node->next = sort_array[i+1].node;
+ sort_array[i+1].node->prev = sort_array[i].node;
+ }
+
+ sort_array[length-1].node->next = NULL;
+ sort_array[0].node->prev = NULL;
+ if (parent)
+ G_NODE (parent->user_data)->children = sort_array[0].node;
+ else
+ G_NODE (tree_store->root)->children = sort_array[0].node;
+
+ /* emit signal */
+ if (parent)
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), parent);
+ else
+ path = gtk_tree_path_new ();
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path,
+ parent, new_order);
+ gtk_tree_path_free (path);
+ g_free (sort_array);
+}
+
+/**
+ * foo_tree_store_swap:
+ * @tree_store: A #FooTreeStore.
+ * @a: A #GtkTreeIter.
+ * @b: Another #GtkTreeIter.
+ *
+ * Swaps @a and @b in the same level of @tree_store. Note that this function
+ * only works with unsorted stores.
+ *
+ * Since: 2.2
+ **/
+void
+foo_tree_store_swap (FooTreeStore *tree_store,
+ GtkTreeIter *a,
+ GtkTreeIter *b)
+{
+ GNode *tmp, *node_a, *node_b, *parent_node;
+ GNode *a_prev, *a_next, *b_prev, *b_next;
+ gint i, a_count, b_count, length, *order;
+ GtkTreePath *path_a, *path_b;
+ GtkTreeIter parent;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (VALID_ITER (a, tree_store));
+ g_return_if_fail (VALID_ITER (b, tree_store));
+
+ node_a = G_NODE (a->user_data);
+ node_b = G_NODE (b->user_data);
+
+ /* basic sanity checking */
+ if (node_a == node_b)
+ return;
+
+ path_a = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), a);
+ path_b = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), b);
+
+ g_return_if_fail (path_a && path_b);
+
+ gtk_tree_path_up (path_a);
+ gtk_tree_path_up (path_b);
+
+ if (gtk_tree_path_get_depth (path_a) == 0
+ || gtk_tree_path_get_depth (path_b) == 0)
+ {
+ if (gtk_tree_path_get_depth (path_a) != gtk_tree_path_get_depth (path_b))
+ {
+ gtk_tree_path_free (path_a);
+ gtk_tree_path_free (path_b);
+
+ g_warning ("Given children are not in the same level\n");
+ return;
+ }
+ parent_node = G_NODE (tree_store->root);
+ }
+ else
+ {
+ if (gtk_tree_path_compare (path_a, path_b))
+ {
+ gtk_tree_path_free (path_a);
+ gtk_tree_path_free (path_b);
+
+ g_warning ("Given children don't have a common parent\n");
+ return;
+ }
+ foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &parent,
+ path_a);
+ parent_node = G_NODE (parent.user_data);
+ }
+ gtk_tree_path_free (path_b);
+
+ /* old links which we have to keep around */
+ a_prev = node_a->prev;
+ a_next = node_a->next;
+
+ b_prev = node_b->prev;
+ b_next = node_b->next;
+
+ /* fix up links if the nodes are next to eachother */
+ if (a_prev == node_b)
+ a_prev = node_a;
+ if (a_next == node_b)
+ a_next = node_a;
+
+ if (b_prev == node_a)
+ b_prev = node_b;
+ if (b_next == node_a)
+ b_next = node_b;
+
+ /* counting nodes */
+ tmp = parent_node->children;
+ i = a_count = b_count = 0;
+ while (tmp)
+ {
+ if (tmp == node_a)
+ a_count = i;
+ if (tmp == node_b)
+ b_count = i;
+
+ tmp = tmp->next;
+ i++;
+ }
+ length = i;
+
+ /* hacking the tree */
+ if (!a_prev)
+ parent_node->children = node_b;
+ else
+ a_prev->next = node_b;
+
+ if (a_next)
+ a_next->prev = node_b;
+
+ if (!b_prev)
+ parent_node->children = node_a;
+ else
+ b_prev->next = node_a;
+
+ if (b_next)
+ b_next->prev = node_a;
+
+ node_a->prev = b_prev;
+ node_a->next = b_next;
+
+ node_b->prev = a_prev;
+ node_b->next = a_next;
+
+ /* emit signal */
+ order = g_new (gint, length);
+ for (i = 0; i < length; i++)
+ if (i == a_count)
+ order[i] = b_count;
+ else if (i == b_count)
+ order[i] = a_count;
+ else
+ order[i] = i;
+
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store), path_a,
+ parent_node == tree_store->root
+ ? NULL : &parent, order);
+ gtk_tree_path_free (path_a);
+ g_free (order);
+}
+
+/* WARNING: this function is *incredibly* fragile. Please smashtest after
+ * making changes here.
+ * -Kris
+ */
+static void
+foo_tree_store_move (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *position,
+ gboolean before)
+{
+ GNode *parent, *node, *a, *b, *tmp, *tmp_a, *tmp_b;
+ gint old_pos, new_pos, length, i, *order;
+ GtkTreePath *path = NULL, *tmppath, *pos_path = NULL;
+ GtkTreeIter parent_iter, dst_a, dst_b;
+ gint depth = 0;
+ gboolean handle_b = TRUE;
+
+ g_return_if_fail (FOO_IS_TREE_STORE (tree_store));
+ g_return_if_fail (!FOO_TREE_STORE_IS_SORTED (tree_store));
+ g_return_if_fail (VALID_ITER (iter, tree_store));
+ if (position)
+ g_return_if_fail (VALID_ITER (position, tree_store));
+
+ a = b = NULL;
+
+ /* sanity checks */
+ if (position)
+ {
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ pos_path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store),
+ position);
+
+ /* if before:
+ * moving the iter before path or "path + 1" doesn't make sense
+ * else
+ * moving the iter before path or "path - 1" doesn't make sense
+ */
+ if (!gtk_tree_path_compare (path, pos_path))
+ goto free_paths_and_out;
+
+ if (before)
+ gtk_tree_path_next (path);
+ else
+ gtk_tree_path_prev (path);
+
+ if (!gtk_tree_path_compare (path, pos_path))
+ goto free_paths_and_out;
+
+ if (before)
+ gtk_tree_path_prev (path);
+ else
+ gtk_tree_path_next (path);
+
+ if (gtk_tree_path_get_depth (path) != gtk_tree_path_get_depth (pos_path))
+ {
+ g_warning ("Given children are not in the same level\n");
+
+ goto free_paths_and_out;
+ }
+
+ tmppath = gtk_tree_path_copy (pos_path);
+ gtk_tree_path_up (path);
+ gtk_tree_path_up (tmppath);
+
+ if (gtk_tree_path_get_depth (path) > 0 &&
+ gtk_tree_path_compare (path, tmppath))
+ {
+ g_warning ("Given children are not in the same level\n");
+
+ gtk_tree_path_free (tmppath);
+ goto free_paths_and_out;
+ }
+
+ gtk_tree_path_free (tmppath);
+ }
+
+ if (!path)
+ {
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_path_up (path);
+ }
+
+ depth = gtk_tree_path_get_depth (path);
+
+ if (depth)
+ {
+ foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store),
+ &parent_iter, path);
+
+ parent = G_NODE (parent_iter.user_data);
+ }
+ else
+ parent = G_NODE (tree_store->root);
+
+ /* yes, I know that this can be done shorter, but I'm doing it this way
+ * so the code is also maintainable
+ */
+
+ if (before && position)
+ {
+ b = G_NODE (position->user_data);
+
+ if (gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path) - 1] > 0)
+ {
+ gtk_tree_path_prev (pos_path);
+ if (foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store),
+ &dst_a, pos_path))
+ a = G_NODE (dst_a.user_data);
+ else
+ a = NULL;
+ gtk_tree_path_next (pos_path);
+ }
+
+ /* if b is NULL, a is NULL too -- we are at the beginning of the list
+ * yes and we leak memory here ...
+ */
+ g_return_if_fail (b);
+ }
+ else if (before && !position)
+ {
+ /* move before without position is appending */
+ a = NULL;
+ b = NULL;
+ }
+ else /* !before */
+ {
+ if (position)
+ a = G_NODE (position->user_data);
+ else
+ a = NULL;
+
+ if (position)
+ {
+ gtk_tree_path_next (pos_path);
+ if (foo_tree_store_get_iter (GTK_TREE_MODEL (tree_store), &dst_b, pos_path))
+ b = G_NODE (dst_b.user_data);
+ else
+ b = NULL;
+ gtk_tree_path_prev (pos_path);
+ }
+ else
+ {
+ /* move after without position is prepending */
+ if (depth)
+ foo_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b,
+ &parent_iter);
+ else
+ foo_tree_store_iter_children (GTK_TREE_MODEL (tree_store), &dst_b,
+ NULL);
+
+ b = G_NODE (dst_b.user_data);
+ }
+
+ /* if a is NULL, b is NULL too -- we are at the end of the list
+ * yes and we leak memory here ...
+ */
+ if (position)
+ g_return_if_fail (a);
+ }
+
+ /* counting nodes */
+ tmp = parent->children;
+
+ length = old_pos = 0;
+ while (tmp)
+ {
+ if (tmp == iter->user_data)
+ old_pos = length;
+
+ tmp = tmp->next;
+ length++;
+ }
+
+ /* remove node from list */
+ node = G_NODE (iter->user_data);
+ tmp_a = node->prev;
+ tmp_b = node->next;
+
+ if (tmp_a)
+ tmp_a->next = tmp_b;
+ else
+ parent->children = tmp_b;
+
+ if (tmp_b)
+ tmp_b->prev = tmp_a;
+
+ /* and reinsert the node */
+ if (a)
+ {
+ tmp = a->next;
+
+ a->next = node;
+ node->next = tmp;
+ node->prev = a;
+ }
+ else if (!a && !before)
+ {
+ tmp = parent->children;
+
+ node->prev = NULL;
+ parent->children = node;
+
+ node->next = tmp;
+ if (tmp)
+ tmp->prev = node;
+
+ handle_b = FALSE;
+ }
+ else if (!a && before)
+ {
+ if (!position)
+ {
+ node->parent = NULL;
+ node->next = node->prev = NULL;
+
+ /* before with sibling = NULL appends */
+ g_node_insert_before (parent, NULL, node);
+ }
+ else
+ {
+ node->parent = NULL;
+ node->next = node->prev = NULL;
+
+ /* after with sibling = NULL prepends */
+ g_node_insert_after (parent, NULL, node);
+ }
+
+ handle_b = FALSE;
+ }
+
+ if (handle_b)
+ {
+ if (b)
+ {
+ tmp = b->prev;
+
+ b->prev = node;
+ node->prev = tmp;
+ node->next = b;
+ }
+ else if (!(!a && before)) /* !a && before is completely handled above */
+ node->next = NULL;
+ }
+
+ /* emit signal */
+ if (position)
+ new_pos = gtk_tree_path_get_indices (pos_path)[gtk_tree_path_get_depth (pos_path)-1];
+ else if (before)
+ {
+ if (depth)
+ new_pos = foo_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store),
+ &parent_iter) - 1;
+ else
+ new_pos = foo_tree_store_iter_n_children (GTK_TREE_MODEL (tree_store),
+ NULL) - 1;
+ }
+ else
+ new_pos = 0;
+
+ if (new_pos > old_pos)
+ {
+ if (before && position)
+ new_pos--;
+ }
+ else
+ {
+ if (!before && position)
+ new_pos++;
+ }
+
+ order = g_new (gint, length);
+ if (new_pos > old_pos)
+ {
+ for (i = 0; i < length; i++)
+ if (i < old_pos)
+ order[i] = i;
+ else if (i >= old_pos && i < new_pos)
+ order[i] = i + 1;
+ else if (i == new_pos)
+ order[i] = old_pos;
+ else
+ order[i] = i;
+ }
+ else
+ {
+ for (i = 0; i < length; i++)
+ if (i == new_pos)
+ order[i] = old_pos;
+ else if (i > new_pos && i <= old_pos)
+ order[i] = i - 1;
+ else
+ order[i] = i;
+ }
+
+ if (depth)
+ {
+ tmppath = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store),
+ &parent_iter);
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store),
+ tmppath, &parent_iter, order);
+ }
+ else
+ {
+ tmppath = gtk_tree_path_new ();
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store),
+ tmppath, NULL, order);
+ }
+
+ gtk_tree_path_free (tmppath);
+ gtk_tree_path_free (path);
+ if (position)
+ gtk_tree_path_free (pos_path);
+ g_free (order);
+
+ return;
+
+free_paths_and_out:
+ gtk_tree_path_free (path);
+ gtk_tree_path_free (pos_path);
+}
+
+/**
+ * foo_tree_store_move_before:
+ * @tree_store: A #FooTreeStore.
+ * @iter: A #GtkTreeIter.
+ * @position: A #GtkTreeIter or %NULL.
+ *
+ * Moves @iter in @tree_store to the position before @position. @iter and
+ * @position should be in the same level. Note that this function only
+ * works with unsorted stores. If @position is %NULL, @iter will be
+ * moved to the end of the level.
+ *
+ * Since: 2.2
+ **/
+void
+foo_tree_store_move_before (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *position)
+{
+ foo_tree_store_move (tree_store, iter, position, TRUE);
+}
+
+/**
+ * foo_tree_store_move_after:
+ * @tree_store: A #FooTreeStore.
+ * @iter: A #GtkTreeIter.
+ * @position: A #GtkTreeIter.
+ *
+ * Moves @iter in @tree_store to the position after @position. @iter and
+ * @position should be in the same level. Note that this function only
+ * works with unsorted stores. If @position is %NULL, @iter will be moved
+ * to the start of the level.
+ *
+ * Since: 2.2
+ **/
+void
+foo_tree_store_move_after (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *position)
+{
+ foo_tree_store_move (tree_store, iter, position, FALSE);
+}
+
+/* Sorting */
+static gint
+foo_tree_store_compare_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ FooTreeStore *tree_store = user_data;
+ GNode *node_a;
+ GNode *node_b;
+ GtkTreeIterCompareFunc func;
+ gpointer data;
+
+ GtkTreeIter iter_a;
+ GtkTreeIter iter_b;
+ gint retval;
+
+ if (tree_store->sort_column_id != -1)
+ {
+ GtkTreeDataSortHeader *header;
+
+ header = _foo_tree_data_list_get_header (tree_store->sort_list,
+ tree_store->sort_column_id);
+ g_return_val_if_fail (header != NULL, 0);
+ g_return_val_if_fail (header->func != NULL, 0);
+
+ func = header->func;
+ data = header->data;
+ }
+ else
+ {
+ g_return_val_if_fail (tree_store->default_sort_func != NULL, 0);
+ func = tree_store->default_sort_func;
+ data = tree_store->default_sort_data;
+ }
+
+ node_a = ((SortTuple *) a)->node;
+ node_b = ((SortTuple *) b)->node;
+
+ iter_a.stamp = tree_store->stamp;
+ iter_a.user_data = node_a;
+ iter_b.stamp = tree_store->stamp;
+ iter_b.user_data = node_b;
+
+ retval = (* func) (GTK_TREE_MODEL (user_data), &iter_a, &iter_b, data);
+
+ if (tree_store->order == GTK_SORT_DESCENDING)
+ {
+ if (retval > 0)
+ retval = -1;
+ else if (retval < 0)
+ retval = 1;
+ }
+ return retval;
+}
+
+static void
+foo_tree_store_sort_helper (FooTreeStore *tree_store,
+ GNode *parent,
+ gboolean recurse)
+{
+ GtkTreeIter iter;
+ GArray *sort_array;
+ GNode *node;
+ GNode *tmp_node;
+ gint list_length;
+ gint i;
+ gint *new_order;
+ GtkTreePath *path;
+
+ node = parent->children;
+ if (node == NULL || node->next == NULL)
+ {
+ if (recurse && node && node->children)
+ foo_tree_store_sort_helper (tree_store, node, TRUE);
+
+ return;
+ }
+
+ list_length = 0;
+ for (tmp_node = node; tmp_node; tmp_node = tmp_node->next)
+ list_length++;
+
+ sort_array = g_array_sized_new (FALSE, FALSE, sizeof (SortTuple), list_length);
+
+ i = 0;
+ for (tmp_node = node; tmp_node; tmp_node = tmp_node->next)
+ {
+ SortTuple tuple;
+
+ tuple.offset = i;
+ tuple.node = tmp_node;
+ g_array_append_val (sort_array, tuple);
+ i++;
+ }
+
+ /* Sort the array */
+ g_array_sort_with_data (sort_array, foo_tree_store_compare_func, tree_store);
+
+ for (i = 0; i < list_length - 1; i++)
+ {
+ g_array_index (sort_array, SortTuple, i).node->next =
+ g_array_index (sort_array, SortTuple, i + 1).node;
+ g_array_index (sort_array, SortTuple, i + 1).node->prev =
+ g_array_index (sort_array, SortTuple, i).node;
+ }
+ g_array_index (sort_array, SortTuple, list_length - 1).node->next = NULL;
+ g_array_index (sort_array, SortTuple, 0).node->prev = NULL;
+ parent->children = g_array_index (sort_array, SortTuple, 0).node;
+
+ /* Let the world know about our new order */
+ new_order = g_new (gint, list_length);
+ for (i = 0; i < list_length; i++)
+ new_order[i] = g_array_index (sort_array, SortTuple, i).offset;
+
+ iter.stamp = tree_store->stamp;
+ iter.user_data = parent;
+
+ if (g_signal_has_handler_pending (tree_store, tree_store->row_inserted_id, 0, FALSE))
+ {
+ path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), &iter);
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store),
+ path, &iter, new_order);
+ gtk_tree_path_free (path);
+ }
+
+ g_free (new_order);
+ g_array_free (sort_array, TRUE);
+
+ if (recurse)
+ {
+ for (tmp_node = parent->children; tmp_node; tmp_node = tmp_node->next)
+ {
+ if (tmp_node->children)
+ foo_tree_store_sort_helper (tree_store, tmp_node, TRUE);
+ }
+ }
+}
+
+static void
+foo_tree_store_sort (FooTreeStore *tree_store)
+{
+ if (!FOO_TREE_STORE_IS_SORTED (tree_store))
+ return;
+
+ if (tree_store->sort_column_id != -1)
+ {
+ GtkTreeDataSortHeader *header = NULL;
+
+ header = _foo_tree_data_list_get_header (tree_store->sort_list,
+ tree_store->sort_column_id);
+
+ /* We want to make sure that we have a function */
+ g_return_if_fail (header != NULL);
+ g_return_if_fail (header->func != NULL);
+ }
+ else
+ {
+ g_return_if_fail (tree_store->default_sort_func != NULL);
+ }
+
+ foo_tree_store_sort_helper (tree_store, G_NODE (tree_store->root), TRUE);
+}
+
+static void
+foo_tree_store_sort_iter_changed (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint column,
+ gboolean emit_signal)
+{
+ GNode *prev = NULL;
+ GNode *next = NULL;
+ GNode *node;
+ GtkTreePath *tmp_path;
+ GtkTreeIter tmp_iter;
+ gint cmp_a = 0;
+ gint cmp_b = 0;
+ gint i;
+ gint old_location;
+ gint new_location;
+ gint *new_order;
+ gint length;
+ GtkTreeIterCompareFunc func;
+ gpointer data;
+
+ g_return_if_fail (G_NODE (iter->user_data)->parent != NULL);
+
+ tmp_iter.stamp = tree_store->stamp;
+ if (tree_store->sort_column_id != -1)
+ {
+ GtkTreeDataSortHeader *header;
+ header = _foo_tree_data_list_get_header (tree_store->sort_list,
+ tree_store->sort_column_id);
+ g_return_if_fail (header != NULL);
+ g_return_if_fail (header->func != NULL);
+ func = header->func;
+ data = header->data;
+ }
+ else
+ {
+ g_return_if_fail (tree_store->default_sort_func != NULL);
+ func = tree_store->default_sort_func;
+ data = tree_store->default_sort_data;
+ }
+
+ /* If it's the built in function, we don't sort. */
+ if (func == _foo_tree_data_list_compare_func &&
+ tree_store->sort_column_id != column)
+ return;
+
+ old_location = 0;
+ node = G_NODE (iter->user_data)->parent->children;
+ /* First we find the iter, its prev, and its next */
+ while (node)
+ {
+ if (node == G_NODE (iter->user_data))
+ break;
+ old_location++;
+ node = node->next;
+ }
+ g_assert (node != NULL);
+
+ prev = node->prev;
+ next = node->next;
+
+ /* Check the common case, where we don't need to sort it moved. */
+ if (prev != NULL)
+ {
+ tmp_iter.user_data = prev;
+ cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data);
+ }
+
+ if (next != NULL)
+ {
+ tmp_iter.user_data = next;
+ cmp_b = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data);
+ }
+
+ if (tree_store->order == GTK_SORT_DESCENDING)
+ {
+ if (cmp_a < 0)
+ cmp_a = 1;
+ else if (cmp_a > 0)
+ cmp_a = -1;
+
+ if (cmp_b < 0)
+ cmp_b = 1;
+ else if (cmp_b > 0)
+ cmp_b = -1;
+ }
+
+ if (prev == NULL && cmp_b <= 0)
+ return;
+ else if (next == NULL && cmp_a <= 0)
+ return;
+ else if (prev != NULL && next != NULL &&
+ cmp_a <= 0 && cmp_b <= 0)
+ return;
+
+ /* We actually need to sort it */
+ /* First, remove the old link. */
+
+ if (prev)
+ prev->next = next;
+ else
+ node->parent->children = next;
+
+ if (next)
+ next->prev = prev;
+
+ node->prev = NULL;
+ node->next = NULL;
+
+ /* FIXME: as an optimization, we can potentially start at next */
+ prev = NULL;
+ node = node->parent->children;
+ new_location = 0;
+ tmp_iter.user_data = node;
+ if (tree_store->order == GTK_SORT_DESCENDING)
+ cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data);
+ else
+ cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data);
+
+ while ((node->next) && (cmp_a > 0))
+ {
+ prev = node;
+ node = node->next;
+ new_location++;
+ tmp_iter.user_data = node;
+ if (tree_store->order == GTK_SORT_DESCENDING)
+ cmp_a = (* func) (GTK_TREE_MODEL (tree_store), &tmp_iter, iter, data);
+ else
+ cmp_a = (* func) (GTK_TREE_MODEL (tree_store), iter, &tmp_iter, data);
+ }
+
+ if ((!node->next) && (cmp_a > 0))
+ {
+ new_location++;
+ node->next = G_NODE (iter->user_data);
+ node->next->prev = node;
+ }
+ else if (prev)
+ {
+ prev->next = G_NODE (iter->user_data);
+ prev->next->prev = prev;
+ G_NODE (iter->user_data)->next = node;
+ G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data);
+ }
+ else
+ {
+ G_NODE (iter->user_data)->next = G_NODE (iter->user_data)->parent->children;
+ G_NODE (iter->user_data)->next->prev = G_NODE (iter->user_data);
+ G_NODE (iter->user_data)->parent->children = G_NODE (iter->user_data);
+ }
+
+ if (!emit_signal)
+ return;
+
+ /* Emit the reordered signal. */
+ length = g_node_n_children (node->parent);
+ new_order = g_new (int, length);
+ if (old_location < new_location)
+ for (i = 0; i < length; i++)
+ {
+ if (i < old_location ||
+ i > new_location)
+ new_order[i] = i;
+ else if (i >= old_location &&
+ i < new_location)
+ new_order[i] = i + 1;
+ else if (i == new_location)
+ new_order[i] = old_location;
+ }
+ else
+ for (i = 0; i < length; i++)
+ {
+ if (i < new_location ||
+ i > old_location)
+ new_order[i] = i;
+ else if (i > new_location &&
+ i <= old_location)
+ new_order[i] = i - 1;
+ else if (i == new_location)
+ new_order[i] = old_location;
+ }
+
+ tmp_iter.user_data = node->parent;
+ tmp_path = foo_tree_store_get_path (GTK_TREE_MODEL (tree_store), &tmp_iter);
+
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (tree_store),
+ tmp_path, &tmp_iter,
+ new_order);
+
+ gtk_tree_path_free (tmp_path);
+ g_free (new_order);
+}
+
+
+static gboolean
+foo_tree_store_get_sort_column_id (GtkTreeSortable *sortable,
+ gint *sort_column_id,
+ GtkSortType *order)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) sortable;
+
+ if (sort_column_id)
+ * sort_column_id = tree_store->sort_column_id;
+ if (order)
+ * order = tree_store->order;
+
+ if (tree_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
+ tree_store->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+foo_tree_store_set_sort_column_id (GtkTreeSortable *sortable,
+ gint sort_column_id,
+ GtkSortType order)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) sortable;
+
+
+ if ((tree_store->sort_column_id == sort_column_id) &&
+ (tree_store->order == order))
+ return;
+
+ if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
+ {
+ if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
+ {
+ GtkTreeDataSortHeader *header = NULL;
+
+ header = _foo_tree_data_list_get_header (tree_store->sort_list,
+ sort_column_id);
+
+ /* We want to make sure that we have a function */
+ g_return_if_fail (header != NULL);
+ g_return_if_fail (header->func != NULL);
+ }
+ else
+ {
+ g_return_if_fail (tree_store->default_sort_func != NULL);
+ }
+ }
+
+ tree_store->sort_column_id = sort_column_id;
+ tree_store->order = order;
+
+ gtk_tree_sortable_sort_column_changed (sortable);
+
+ foo_tree_store_sort (tree_store);
+}
+
+static void
+foo_tree_store_set_sort_func (GtkTreeSortable *sortable,
+ gint sort_column_id,
+ GtkTreeIterCompareFunc func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) sortable;
+
+ tree_store->sort_list = _foo_tree_data_list_set_header (tree_store->sort_list,
+ sort_column_id,
+ func, data, destroy);
+
+ if (tree_store->sort_column_id == sort_column_id)
+ foo_tree_store_sort (tree_store);
+}
+
+static void
+foo_tree_store_set_default_sort_func (GtkTreeSortable *sortable,
+ GtkTreeIterCompareFunc func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) sortable;
+
+ if (tree_store->default_sort_destroy)
+ {
+ GDestroyNotify d = tree_store->default_sort_destroy;
+
+ tree_store->default_sort_destroy = NULL;
+ d (tree_store->default_sort_data);
+ }
+
+ tree_store->default_sort_func = func;
+ tree_store->default_sort_data = data;
+ tree_store->default_sort_destroy = destroy;
+
+ if (tree_store->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
+ foo_tree_store_sort (tree_store);
+}
+
+static gboolean
+foo_tree_store_has_default_sort_func (GtkTreeSortable *sortable)
+{
+ FooTreeStore *tree_store = (FooTreeStore *) sortable;
+
+ return (tree_store->default_sort_func != NULL);
+}
+
+static void
+validate_gnode (GNode* node)
+{
+ GNode *iter;
+
+ iter = node->children;
+ while (iter != NULL)
+ {
+ g_assert (iter->parent == node);
+ if (iter->prev)
+ g_assert (iter->prev->next == iter);
+ validate_gnode (iter);
+ iter = iter->next;
+ }
+}
+
+/* GtkBuildable custom tag implementation
+ *
+ * <columns>
+ * <column type="..."/>
+ * <column type="..."/>
+ * </columns>
+ */
+typedef struct {
+ GtkBuilder *builder;
+ GObject *object;
+ GSList *items;
+} GSListSubParserData;
+
+static void
+tree_model_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ gpointer user_data,
+ GError **error)
+{
+ guint i;
+ GSListSubParserData *data = (GSListSubParserData*)user_data;
+
+ for (i = 0; names[i]; i++)
+ {
+ if (strcmp (names[i], "type") == 0)
+ data->items = g_slist_prepend (data->items, g_strdup (values[i]));
+ }
+}
+
+static void
+tree_model_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GSListSubParserData *data = (GSListSubParserData*)user_data;
+
+ g_assert(data->builder);
+
+ if (strcmp (element_name, "columns") == 0)
+ {
+ GSList *l;
+ GType *types;
+ int i;
+ GType type;
+
+ data = (GSListSubParserData*)user_data;
+ data->items = g_slist_reverse (data->items);
+ types = g_new0 (GType, g_slist_length (data->items));
+
+ for (l = data->items, i = 0; l; l = l->next, i++)
+ {
+ type = gtk_builder_get_type_from_name (data->builder, l->data);
+ if (type == G_TYPE_INVALID)
+ {
+ g_warning ("Unknown type %s specified in treemodel %s",
+ (const gchar*)l->data,
+ gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
+ continue;
+ }
+ types[i] = type;
+
+ g_free (l->data);
+ }
+
+ foo_tree_store_set_column_types (FOO_TREE_STORE (data->object), i, types);
+
+ g_free (types);
+ }
+ else if (strcmp (element_name, "column") == 0)
+ ;
+}
+
+static const GMarkupParser tree_model_parser =
+ {
+ tree_model_start_element,
+ tree_model_end_element
+ };
+
+
+static gboolean
+foo_tree_store_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data)
+{
+ GSListSubParserData *parser_data;
+
+ if (child)
+ return FALSE;
+
+ if (strcmp (tagname, "columns") == 0)
+ {
+ parser_data = g_slice_new0 (GSListSubParserData);
+ parser_data->builder = builder;
+ parser_data->items = NULL;
+ parser_data->object = G_OBJECT (buildable);
+
+ *parser = tree_model_parser;
+ *data = parser_data;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+foo_tree_store_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data)
+{
+ GSListSubParserData *data;
+
+ if (strcmp (tagname, "columns"))
+ return;
+
+ data = (GSListSubParserData*)user_data;
+
+ g_slist_free (data->items);
+ g_slice_free (GSListSubParserData, data);
+}
diff --git a/footreestore.h b/footreestore.h
new file mode 100644
index 0000000..04b3d8e
--- /dev/null
+++ b/footreestore.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FOO_TREE_STORE_H__
+#define __FOO_TREE_STORE_H__
+
+#include <gtk/gtk.h>
+#include <stdarg.h>
+
+
+G_BEGIN_DECLS
+
+
+#define FOO_TYPE_TREE_STORE (foo_tree_store_get_type ())
+#define FOO_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_TREE_STORE, FooTreeStore))
+#define FOO_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_TREE_STORE, FooTreeStoreClass))
+#define FOO_IS_TREE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_TREE_STORE))
+#define FOO_IS_TREE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_TREE_STORE))
+#define FOO_TREE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_TREE_STORE, FooTreeStoreClass))
+
+typedef struct _FooTreeStore FooTreeStore;
+typedef struct _FooTreeStoreClass FooTreeStoreClass;
+
+struct _FooTreeStore
+{
+ GObject parent;
+
+ gint stamp;
+ gpointer root;
+ gpointer last;
+ gint n_columns;
+ gint sort_column_id;
+ GList *sort_list;
+ GtkSortType order;
+ GType *column_headers;
+ GtkTreeIterCompareFunc default_sort_func;
+ gpointer default_sort_data;
+ GDestroyNotify default_sort_destroy;
+
+ guint row_changed_id;
+ guint row_inserted_id;
+ guint row_has_child_toggled_id;
+ guint rows_reordered_id;
+
+ guint (columns_dirty) : 1;
+};
+
+struct _FooTreeStoreClass
+{
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+
+GType foo_tree_store_get_type (void) G_GNUC_CONST;
+FooTreeStore *foo_tree_store_new (gint n_columns,
+ ...);
+FooTreeStore *foo_tree_store_newv (gint n_columns,
+ GType *types);
+void foo_tree_store_set_column_types (FooTreeStore *tree_store,
+ gint n_columns,
+ GType *types);
+
+/* NOTE: use gtk_tree_model_get to get values from a FooTreeStore */
+
+void foo_tree_store_set_value (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+void foo_tree_store_set (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ ...);
+void foo_tree_store_set_valuesv (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ gint *columns,
+ GValue *values,
+ gint n_values);
+void foo_tree_store_set_valist (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ va_list var_args);
+gboolean foo_tree_store_remove (FooTreeStore *tree_store,
+ GtkTreeIter *iter);
+void foo_tree_store_insert (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint position);
+void foo_tree_store_insert_before (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ GtkTreeIter *sibling);
+void foo_tree_store_insert_after (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ GtkTreeIter *sibling);
+void foo_tree_store_insert_with_values (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint position,
+ ...);
+void foo_tree_store_insert_with_valuesv (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint position,
+ gint *columns,
+ GValue *values,
+ gint n_values);
+void foo_tree_store_prepend (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+void foo_tree_store_append (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+gboolean foo_tree_store_is_ancestor (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *descendant);
+gint foo_tree_store_iter_depth (FooTreeStore *tree_store,
+ GtkTreeIter *iter);
+void foo_tree_store_clear (FooTreeStore *tree_store);
+gboolean foo_tree_store_iter_is_valid (FooTreeStore *tree_store,
+ GtkTreeIter *iter);
+void foo_tree_store_reorder (FooTreeStore *tree_store,
+ GtkTreeIter *parent,
+ gint *new_order);
+void foo_tree_store_swap (FooTreeStore *tree_store,
+ GtkTreeIter *a,
+ GtkTreeIter *b);
+void foo_tree_store_move_before (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *position);
+void foo_tree_store_move_after (FooTreeStore *tree_store,
+ GtkTreeIter *iter,
+ GtkTreeIter *position);
+
+
+G_END_DECLS
+
+
+#endif /* __FOO_TREE_STORE_H__ */
diff --git a/module/Makefile b/module/Makefile
deleted file mode 100644
index bc49ed0..0000000
--- a/module/Makefile
+++ /dev/null
@@ -1,71 +0,0 @@
-ifneq ($(KERNELRELEASE),)
-
-obj-m := sysprof-module.o
-
-else
-
-ifeq ($(PREFIX),)
-PREFIX := /usr/local
-endif
-
-MODULE := sysprof-module
-KDIR := /lib/modules/$(shell uname -r)/build
-INCLUDE := -isystem $(KDIR)/include
-MODCFLAGS := -DMODULE -D__KERNEL__ -Wall ${INCLUDE}
-
-KMAKE := $(MAKE) -C $(KDIR) SUBDIRS=$(PWD)
-
-modules: $(MODULE).o
-
-insert_module: install
- modprobe -r sysprof-module
- modprobe sysprof-module
-
-ifneq ($(shell (uname -r | grep 2.6) > /dev/null ; echo -n $$?),0)
- echo A 2.6 kernel is required; exit 1
-endif
-
-# build module
-
-$(MODULE).o: $(MODULE).c
- $(KMAKE) modules
-
-
-# Automake rules, as per "Third-Party Makefiles" in the automake manual
-
-all: $(MODULE).o
-
-distdir:
- cp sysprof-module.c $(distdir)
- cp sysprof-module.h $(distdir)
- cp Makefile $(distdir)
-
-install:
- $(KMAKE) modules_install
- [ -e /sbin/depmod ] && /sbin/depmod
-
-install-data:
-install-exec:
-uninstall:
-
-install-info:
-installdirs:
-check:
-installcheck:
-mostlyclean:
-clean:
- rm -f sysprof-module.ko
- rm -f sysprof-module.o
- rm -f sysprof-module.mod.o
- rm -f sysprof-module.mod.c
-distclean: clean
-maintainer-clean:
-dvi:
-pdf:
-info:
-html:
-tags:
-ctags:
-
-endif
-
diff --git a/module/sysprof-module.c b/module/sysprof-module.c
deleted file mode 100644
index 77e1e06..0000000
--- a/module/sysprof-module.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/* -*- c-basic-offset: 8 -*- */
-
-/* Sysprof -- Sampling, systemwide CPU profiler
- * Copyright 2004, Red Hat, Inc.
- * Copyright 2004, 2005, 2006, 2007, 2008, Soeren Sandmann
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifdef CONFIG_SMP
-# define __SMP__
-#endif
-#include <asm/atomic.h>
-#include <linux/kernel.h> /* Needed for KERN_ALERT */
-#include <linux/module.h> /* Needed by all modules */
-#include <linux/sched.h>
-
-#include <linux/proc_fs.h>
-#include <asm/uaccess.h>
-#include <linux/poll.h>
-#include <linux/highmem.h>
-#include <linux/pagemap.h>
-#include <linux/profile.h>
-
-#include "sysprof-module.h"
-
-#include "../config.h"
-
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-#include <linux/config.h>
-#endif
-
-#if !CONFIG_PROFILING
-# error Sysprof needs a kernel with profiling support compiled in.
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
-# error Sysprof needs a Linux 2.6.11 kernel or later
-#endif
-#include <linux/kallsyms.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Soeren Sandmann (sandmann@daimi.au.dk)");
-
-#define SAMPLES_PER_SECOND (200)
-#define INTERVAL ((HZ <= SAMPLES_PER_SECOND)? 1 : (HZ / SAMPLES_PER_SECOND))
-#define N_TRACES 256
-
-static SysprofStackTrace stack_traces[N_TRACES];
-static SysprofStackTrace * head = &stack_traces[0];
-static SysprofStackTrace * tail = &stack_traces[0];
-DECLARE_WAIT_QUEUE_HEAD (wait_for_trace);
-DECLARE_WAIT_QUEUE_HEAD (wait_for_exit);
-
-/* Macro the names of the registers that are used on each architecture */
-#if !defined(CONFIG_X86_64) && !defined(CONFIG_X86)
-# error Sysprof only supports the i386 and x86-64 architectures
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,6,25)
-# define REG_FRAME_PTR bp
-# define REG_INS_PTR ip
-# define REG_STACK_PTR sp
-# define REG_STACK_PTR0 sp0
-#else
-# if defined(CONFIG_X86_64)
-# define REG_FRAME_PTR rbp
-# define REG_INS_PTR rip
-# define REG_STACK_PTR rsp
-# define REG_STACK_PTR0 rsp0
-# else
-# define REG_FRAME_PTR ebp
-# define REG_INS_PTR eip
-# define REG_STACK_PTR esp
-# define REG_STACK_PTR0 esp0
-# endif
-#endif
-
-typedef struct userspace_reader userspace_reader;
-struct userspace_reader
-{
- struct task_struct *task;
- unsigned long cache_address;
- unsigned long *cache;
-};
-
-typedef struct StackFrame StackFrame;
-struct StackFrame {
- unsigned long next;
- unsigned long return_address;
-};
-
-struct work_struct work;
-
-static int
-read_frame (void *frame_pointer, StackFrame *frame)
-{
-#if 0
- /* This is commented out because we seem to be called with
- * (current_thread_info()->addr_limit.seg)) == 0
- * which means access_ok() _always_ fails.
- *
- * Not sure why (or if) this isn't the case for oprofile
- */
- if (!access_ok(VERIFY_READ, frame_pointer, sizeof(StackFrame)))
- return 1;
-#endif
-
- if (__copy_from_user_inatomic (
- frame, frame_pointer, sizeof (StackFrame)))
- return 1;
-
- return 0;
-}
-
-DEFINE_PER_CPU(int, n_samples);
-
-static int
-timer_notify (struct pt_regs *regs)
-{
- SysprofStackTrace *trace = head;
- int i;
- int is_user;
- static atomic_t in_timer_notify = ATOMIC_INIT(1);
- int n;
-
- n = ++get_cpu_var(n_samples);
- put_cpu_var(n_samples);
-
- if (n % INTERVAL != 0)
- return 0;
-
- /* 0: locked, 1: unlocked */
-
- if (!atomic_dec_and_test(&in_timer_notify))
- goto out;
-
- is_user = user_mode(regs);
-
- if (!current || current->pid == 0)
- goto out;
-
- if (is_user && current->state != TASK_RUNNING)
- goto out;
-
- if (!is_user)
- {
- /* kernel */
-
- trace->pid = current->pid;
- trace->truncated = 0;
- trace->n_addresses = 1;
-
- /* 0x1 is taken by sysprof to mean "in kernel" */
- trace->addresses[0] = (void *)0x1;
- }
- else
- {
- StackFrame *frame_pointer;
- StackFrame frame;
- memset(trace, 0, sizeof (SysprofStackTrace));
-
- trace->pid = current->pid;
- trace->truncated = 0;
-
- i = 0;
-
- trace->addresses[i++] = (void *)regs->REG_INS_PTR;
-
- frame_pointer = (void *)regs->REG_FRAME_PTR;
-
- while (read_frame (frame_pointer, &frame) == 0 &&
- i < SYSPROF_MAX_ADDRESSES &&
- (unsigned long)frame_pointer >= regs->REG_STACK_PTR)
- {
- trace->addresses[i++] = (void *)frame.return_address;
- frame_pointer = (StackFrame *)frame.next;
- }
-
- trace->n_addresses = i;
-
- if (i == SYSPROF_MAX_ADDRESSES)
- trace->truncated = 1;
- else
- trace->truncated = 0;
- }
-
- if (head++ == &stack_traces[N_TRACES - 1])
- head = &stack_traces[0];
-
- wake_up (&wait_for_trace);
-
-out:
- atomic_inc(&in_timer_notify);
- return 0;
-}
-
-static int
-procfile_read(char *buffer,
- char **buffer_location,
- off_t offset,
- int buffer_len,
- int *eof,
- void *data)
-{
- if (head == tail)
- return -EWOULDBLOCK;
-
- *buffer_location = (char *)tail;
-
- BUG_ON(tail->pid == 0);
-
- if (tail++ == &stack_traces[N_TRACES - 1])
- tail = &stack_traces[0];
-
- return sizeof (SysprofStackTrace);
-}
-
-struct proc_dir_entry *trace_proc_file;
-static unsigned int
-procfile_poll(struct file *filp, poll_table *poll_table)
-{
- if (head != tail)
- return POLLIN | POLLRDNORM;
-
- poll_wait(filp, &wait_for_trace, poll_table);
-
- if (head != tail)
- return POLLIN | POLLRDNORM;
-
- return 0;
-}
-
-int
-init_module(void)
-{
- static struct file_operations fops;
-
- trace_proc_file =
- create_proc_entry ("sysprof-trace", S_IFREG | S_IRUGO, NULL);
-
- if (!trace_proc_file)
- return 1;
-
- fops = *trace_proc_file->proc_fops;
- fops.poll = procfile_poll;
-
- trace_proc_file->read_proc = procfile_read;
- trace_proc_file->proc_fops = &fops;
- trace_proc_file->size = sizeof (SysprofStackTrace);
-
- register_timer_hook (timer_notify);
-
- printk(KERN_ALERT "sysprof: loaded (%s)\n", PACKAGE_VERSION);
-
- return 0;
-}
-
-void
-cleanup_module(void)
-{
- unregister_timer_hook (timer_notify);
-
- remove_proc_entry("sysprof-trace", NULL);
-
- printk(KERN_ALERT "sysprof: unloaded\n");
-}
-
diff --git a/perf_counter.h b/perf_counter.h
new file mode 100644
index 0000000..a22d2cb
--- /dev/null
+++ b/perf_counter.h
@@ -0,0 +1,805 @@
+/*
+ * Performance counters:
+ *
+ * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2009, Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2008-2009, Red Hat, Inc., Peter Zijlstra
+ *
+ * Data type definitions, declarations, prototypes.
+ *
+ * Started by: Thomas Gleixner and Ingo Molnar
+ *
+ * For licencing details see kernel-base/COPYING
+ */
+#ifndef _LINUX_PERF_COUNTER_H
+#define _LINUX_PERF_COUNTER_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/byteorder.h>
+
+/*
+ * User-space ABI bits:
+ */
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+ PERF_TYPE_HW_CACHE = 3,
+ PERF_TYPE_RAW = 4,
+
+ PERF_TYPE_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized performance counter event types, used by the
+ * attr.event_id parameter of the sys_perf_counter_open()
+ * syscall:
+ */
+enum perf_hw_id {
+ /*
+ * Common hardware events, generalized by the kernel:
+ */
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+
+ PERF_COUNT_HW_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized hardware cache counters:
+ *
+ * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x
+ * { read, write, prefetch } x
+ * { accesses, misses }
+ */
+enum perf_hw_cache_id {
+ PERF_COUNT_HW_CACHE_L1D = 0,
+ PERF_COUNT_HW_CACHE_L1I = 1,
+ PERF_COUNT_HW_CACHE_LL = 2,
+ PERF_COUNT_HW_CACHE_DTLB = 3,
+ PERF_COUNT_HW_CACHE_ITLB = 4,
+ PERF_COUNT_HW_CACHE_BPU = 5,
+
+ PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_id {
+ PERF_COUNT_HW_CACHE_OP_READ = 0,
+ PERF_COUNT_HW_CACHE_OP_WRITE = 1,
+ PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
+
+ PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_result_id {
+ PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
+ PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
+
+ PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
+};
+
+/*
+ * Special "software" counters provided by the kernel, even if the hardware
+ * does not support performance counters. These counters measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum perf_sw_ids {
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+
+ PERF_COUNT_SW_MAX, /* non-ABI */
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_counter_sample_format {
+ PERF_SAMPLE_IP = 1U << 0,
+ PERF_SAMPLE_TID = 1U << 1,
+ PERF_SAMPLE_TIME = 1U << 2,
+ PERF_SAMPLE_ADDR = 1U << 3,
+ PERF_SAMPLE_READ = 1U << 4,
+ PERF_SAMPLE_CALLCHAIN = 1U << 5,
+ PERF_SAMPLE_ID = 1U << 6,
+ PERF_SAMPLE_CPU = 1U << 7,
+ PERF_SAMPLE_PERIOD = 1U << 8,
+ PERF_SAMPLE_STREAM_ID = 1U << 9,
+ PERF_SAMPLE_RAW = 1U << 10,
+
+ PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */
+};
+
+/*
+ * The format of the data returned by read() on a perf counter fd,
+ * as specified by attr.read_format:
+ *
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
+ */
+enum perf_counter_read_format {
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ PERF_FORMAT_ID = 1U << 2,
+ PERF_FORMAT_GROUP = 1U << 3,
+
+ PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
+};
+
+#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
+
+/*
+ * Hardware event to monitor via a performance monitoring counter:
+ */
+struct perf_counter_attr {
+
+ /*
+ * Major type: hardware/software/tracepoint/etc.
+ */
+ __u32 type;
+
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ __u32 size;
+
+ /*
+ * Type specific configuration information.
+ */
+ __u64 config;
+
+ union {
+ __u64 sample_period;
+ __u64 sample_freq;
+ };
+
+ __u64 sample_type;
+ __u64 read_format;
+
+ __u64 disabled : 1, /* off by default */
+ inherit : 1, /* children inherit it */
+ pinned : 1, /* must always be on PMU */
+ exclusive : 1, /* only group on PMU */
+ exclude_user : 1, /* don't count user */
+ exclude_kernel : 1, /* ditto kernel */
+ exclude_hv : 1, /* ditto hypervisor */
+ exclude_idle : 1, /* don't count when idle */
+ mmap : 1, /* include mmap data */
+ comm : 1, /* include comm data */
+ freq : 1, /* use freq, not period */
+ inherit_stat : 1, /* per task counts */
+ enable_on_exec : 1, /* next exec enables */
+ task : 1, /* trace fork/exit */
+
+ __reserved_1 : 50;
+
+ __u32 wakeup_events; /* wakeup every n events */
+ __u32 __reserved_2;
+
+ __u64 __reserved_3;
+};
+
+/*
+ * Ioctls that can be done on a perf counter fd:
+ */
+#define PERF_COUNTER_IOC_ENABLE _IO ('$', 0)
+#define PERF_COUNTER_IOC_DISABLE _IO ('$', 1)
+#define PERF_COUNTER_IOC_REFRESH _IO ('$', 2)
+#define PERF_COUNTER_IOC_RESET _IO ('$', 3)
+#define PERF_COUNTER_IOC_PERIOD _IOW('$', 4, u64)
+#define PERF_COUNTER_IOC_SET_OUTPUT _IO ('$', 5)
+
+enum perf_counter_ioc_flags {
+ PERF_IOC_FLAG_GROUP = 1U << 0,
+};
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_counter_mmap_page {
+ __u32 version; /* version number of this structure */
+ __u32 compat_version; /* lowest version this is compat with */
+
+ /*
+ * Bits needed to read the hw counters in user-space.
+ *
+ * u32 seq;
+ * s64 count;
+ *
+ * do {
+ * seq = pc->lock;
+ *
+ * barrier()
+ * if (pc->index) {
+ * count = pmc_read(pc->index - 1);
+ * count += pc->offset;
+ * } else
+ * goto regular_read;
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ *
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ __u32 lock; /* seqlock for synchronization */
+ __u32 index; /* hardware counter identifier */
+ __s64 offset; /* add to hardware counter value */
+ __u64 time_enabled; /* time counter active */
+ __u64 time_running; /* time counter on cpu */
+
+ /*
+ * Hole for extension of the self monitor capabilities
+ */
+
+ __u64 __reserved[123]; /* align to 1k */
+
+ /*
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading the @data_head value should issue an rmb(), on
+ * SMP capable platforms, after reading this value -- see
+ * perf_counter_wakeup().
+ *
+ * When the mapping is PROT_WRITE the @data_tail value should be
+ * written by userspace to reflect the last read data. In this case
+ * the kernel will not over-write unread data.
+ */
+ __u64 data_head; /* head in the data section */
+ __u64 data_tail; /* user-space written tail */
+};
+
+#define PERF_EVENT_MISC_CPUMODE_MASK (3 << 0)
+#define PERF_EVENT_MISC_CPUMODE_UNKNOWN (0 << 0)
+#define PERF_EVENT_MISC_KERNEL (1 << 0)
+#define PERF_EVENT_MISC_USER (2 << 0)
+#define PERF_EVENT_MISC_HYPERVISOR (3 << 0)
+
+struct perf_event_header {
+ __u32 type;
+ __u16 misc;
+ __u16 size;
+};
+
+enum perf_event_type {
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * };
+ */
+ PERF_EVENT_MMAP = 1,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * };
+ */
+ PERF_EVENT_LOST = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * };
+ */
+ PERF_EVENT_COMM = 3,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * };
+ */
+ PERF_EVENT_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * };
+ */
+ PERF_EVENT_THROTTLE = 5,
+ PERF_EVENT_UNTHROTTLE = 6,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * };
+ */
+ PERF_EVENT_FORK = 7,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * };
+ */
+ PERF_EVENT_READ = 8,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values; } && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
+ * };
+ */
+ PERF_EVENT_SAMPLE = 9,
+
+ PERF_EVENT_MAX, /* non-ABI */
+};
+
+enum perf_callchain_context {
+ PERF_CONTEXT_HV = (__u64)-32,
+ PERF_CONTEXT_KERNEL = (__u64)-128,
+ PERF_CONTEXT_USER = (__u64)-512,
+
+ PERF_CONTEXT_GUEST = (__u64)-2048,
+ PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
+ PERF_CONTEXT_GUEST_USER = (__u64)-2560,
+
+ PERF_CONTEXT_MAX = (__u64)-4095,
+};
+
+
+
+
+
+#ifdef __KERNEL__
+/*
+ * Kernel-internal data types and definitions:
+ */
+
+#ifdef CONFIG_PERF_COUNTERS
+# include <asm/perf_counter.h>
+#endif
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/fs.h>
+#include <linux/pid_namespace.h>
+#include <asm/atomic.h>
+
+#define PERF_MAX_STACK_DEPTH 255
+
+struct perf_callchain_entry {
+ __u64 nr;
+ __u64 ip[PERF_MAX_STACK_DEPTH];
+};
+
+struct perf_raw_record {
+ u32 size;
+ void *data;
+};
+
+struct task_struct;
+
+/**
+ * struct hw_perf_counter - performance counter hardware details:
+ */
+struct hw_perf_counter {
+#ifdef CONFIG_PERF_COUNTERS
+ union {
+ struct { /* hardware */
+ u64 config;
+ unsigned long config_base;
+ unsigned long counter_base;
+ int idx;
+ };
+ union { /* software */
+ atomic64_t count;
+ struct hrtimer hrtimer;
+ };
+ };
+ atomic64_t prev_count;
+ u64 sample_period;
+ u64 last_period;
+ atomic64_t period_left;
+ u64 interrupts;
+
+ u64 freq_count;
+ u64 freq_interrupts;
+ u64 freq_stamp;
+#endif
+};
+
+struct perf_counter;
+
+/**
+ * struct pmu - generic performance monitoring unit
+ */
+struct pmu {
+ int (*enable) (struct perf_counter *counter);
+ void (*disable) (struct perf_counter *counter);
+ void (*read) (struct perf_counter *counter);
+ void (*unthrottle) (struct perf_counter *counter);
+};
+
+/**
+ * enum perf_counter_active_state - the states of a counter
+ */
+enum perf_counter_active_state {
+ PERF_COUNTER_STATE_ERROR = -2,
+ PERF_COUNTER_STATE_OFF = -1,
+ PERF_COUNTER_STATE_INACTIVE = 0,
+ PERF_COUNTER_STATE_ACTIVE = 1,
+};
+
+struct file;
+
+struct perf_mmap_data {
+ struct rcu_head rcu_head;
+ int nr_pages; /* nr of data pages */
+ int writable; /* are we writable */
+ int nr_locked; /* nr pages mlocked */
+
+ atomic_t poll; /* POLL_ for wakeups */
+ atomic_t events; /* event limit */
+
+ atomic_long_t head; /* write position */
+ atomic_long_t done_head; /* completed head */
+
+ atomic_t lock; /* concurrent writes */
+ atomic_t wakeup; /* needs a wakeup */
+ atomic_t lost; /* nr records lost */
+
+ struct perf_counter_mmap_page *user_page;
+ void *data_pages[0];
+};
+
+struct perf_pending_entry {
+ struct perf_pending_entry *next;
+ void (*func)(struct perf_pending_entry *);
+};
+
+/**
+ * struct perf_counter - performance counter kernel representation:
+ */
+struct perf_counter {
+#ifdef CONFIG_PERF_COUNTERS
+ struct list_head list_entry;
+ struct list_head event_entry;
+ struct list_head sibling_list;
+ int nr_siblings;
+ struct perf_counter *group_leader;
+ const struct pmu *pmu;
+
+ enum perf_counter_active_state state;
+ atomic64_t count;
+
+ /*
+ * These are the total time in nanoseconds that the counter
+ * has been enabled (i.e. eligible to run, and the task has
+ * been scheduled in, if this is a per-task counter)
+ * and running (scheduled onto the CPU), respectively.
+ *
+ * They are computed from tstamp_enabled, tstamp_running and
+ * tstamp_stopped when the counter is in INACTIVE or ACTIVE state.
+ */
+ u64 total_time_enabled;
+ u64 total_time_running;
+
+ /*
+ * These are timestamps used for computing total_time_enabled
+ * and total_time_running when the counter is in INACTIVE or
+ * ACTIVE state, measured in nanoseconds from an arbitrary point
+ * in time.
+ * tstamp_enabled: the notional time when the counter was enabled
+ * tstamp_running: the notional time when the counter was scheduled on
+ * tstamp_stopped: in INACTIVE state, the notional time when the
+ * counter was scheduled off.
+ */
+ u64 tstamp_enabled;
+ u64 tstamp_running;
+ u64 tstamp_stopped;
+
+ struct perf_counter_attr attr;
+ struct hw_perf_counter hw;
+
+ struct perf_counter_context *ctx;
+ struct file *filp;
+
+ /*
+ * These accumulate total time (in nanoseconds) that children
+ * counters have been enabled and running, respectively.
+ */
+ atomic64_t child_total_time_enabled;
+ atomic64_t child_total_time_running;
+
+ /*
+ * Protect attach/detach and child_list:
+ */
+ struct mutex child_mutex;
+ struct list_head child_list;
+ struct perf_counter *parent;
+
+ int oncpu;
+ int cpu;
+
+ struct list_head owner_entry;
+ struct task_struct *owner;
+
+ /* mmap bits */
+ struct mutex mmap_mutex;
+ atomic_t mmap_count;
+ struct perf_mmap_data *data;
+
+ /* poll related */
+ wait_queue_head_t waitq;
+ struct fasync_struct *fasync;
+
+ /* delayed work for NMIs and such */
+ int pending_wakeup;
+ int pending_kill;
+ int pending_disable;
+ struct perf_pending_entry pending;
+
+ atomic_t event_limit;
+
+ void (*destroy)(struct perf_counter *);
+ struct rcu_head rcu_head;
+
+ struct pid_namespace *ns;
+ u64 id;
+#endif
+};
+
+/**
+ * struct perf_counter_context - counter context structure
+ *
+ * Used as a container for task counters and CPU counters as well:
+ */
+struct perf_counter_context {
+ /*
+ * Protect the states of the counters in the list,
+ * nr_active, and the list:
+ */
+ spinlock_t lock;
+ /*
+ * Protect the list of counters. Locking either mutex or lock
+ * is sufficient to ensure the list doesn't change; to change
+ * the list you need to lock both the mutex and the spinlock.
+ */
+ struct mutex mutex;
+
+ struct list_head counter_list;
+ struct list_head event_list;
+ int nr_counters;
+ int nr_active;
+ int is_active;
+ int nr_stat;
+ atomic_t refcount;
+ struct task_struct *task;
+
+ /*
+ * Context clock, runs when context enabled.
+ */
+ u64 time;
+ u64 timestamp;
+
+ /*
+ * These fields let us detect when two contexts have both
+ * been cloned (inherited) from a common ancestor.
+ */
+ struct perf_counter_context *parent_ctx;
+ u64 parent_gen;
+ u64 generation;
+ int pin_count;
+ struct rcu_head rcu_head;
+};
+
+/**
+ * struct perf_counter_cpu_context - per cpu counter context structure
+ */
+struct perf_cpu_context {
+ struct perf_counter_context ctx;
+ struct perf_counter_context *task_ctx;
+ int active_oncpu;
+ int max_pertask;
+ int exclusive;
+
+ /*
+ * Recursion avoidance:
+ *
+ * task, softirq, irq, nmi context
+ */
+ int recursion[4];
+};
+
+#ifdef CONFIG_PERF_COUNTERS
+
+/*
+ * Set by architecture code:
+ */
+extern int perf_max_counters;
+
+extern const struct pmu *hw_perf_counter_init(struct perf_counter *counter);
+
+extern void perf_counter_task_sched_in(struct task_struct *task, int cpu);
+extern void perf_counter_task_sched_out(struct task_struct *task,
+ struct task_struct *next, int cpu);
+extern void perf_counter_task_tick(struct task_struct *task, int cpu);
+extern int perf_counter_init_task(struct task_struct *child);
+extern void perf_counter_exit_task(struct task_struct *child);
+extern void perf_counter_free_task(struct task_struct *task);
+extern void set_perf_counter_pending(void);
+extern void perf_counter_do_pending(void);
+extern void perf_counter_print_debug(void);
+extern void __perf_disable(void);
+extern bool __perf_enable(void);
+extern void perf_disable(void);
+extern void perf_enable(void);
+extern int perf_counter_task_disable(void);
+extern int perf_counter_task_enable(void);
+extern int hw_perf_group_sched_in(struct perf_counter *group_leader,
+ struct perf_cpu_context *cpuctx,
+ struct perf_counter_context *ctx, int cpu);
+extern void perf_counter_update_userpage(struct perf_counter *counter);
+
+struct perf_sample_data {
+ struct pt_regs *regs;
+ u64 addr;
+ u64 period;
+ struct perf_raw_record *raw;
+};
+
+extern int perf_counter_overflow(struct perf_counter *counter, int nmi,
+ struct perf_sample_data *data);
+extern void perf_counter_output(struct perf_counter *counter, int nmi,
+ struct perf_sample_data *data);
+
+/*
+ * Return 1 for a software counter, 0 for a hardware counter
+ */
+static inline int is_software_counter(struct perf_counter *counter)
+{
+ return (counter->attr.type != PERF_TYPE_RAW) &&
+ (counter->attr.type != PERF_TYPE_HARDWARE) &&
+ (counter->attr.type != PERF_TYPE_HW_CACHE);
+}
+
+extern atomic_t perf_swcounter_enabled[PERF_COUNT_SW_MAX];
+
+extern void __perf_swcounter_event(u32, u64, int, struct pt_regs *, u64);
+
+static inline void
+perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs, u64 addr)
+{
+ if (atomic_read(&perf_swcounter_enabled[event]))
+ __perf_swcounter_event(event, nr, nmi, regs, addr);
+}
+
+extern void __perf_counter_mmap(struct vm_area_struct *vma);
+
+static inline void perf_counter_mmap(struct vm_area_struct *vma)
+{
+ if (vma->vm_flags & VM_EXEC)
+ __perf_counter_mmap(vma);
+}
+
+extern void perf_counter_comm(struct task_struct *tsk);
+extern void perf_counter_fork(struct task_struct *tsk);
+
+extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
+
+extern int sysctl_perf_counter_paranoid;
+extern int sysctl_perf_counter_mlock;
+extern int sysctl_perf_counter_sample_rate;
+
+extern void perf_counter_init(void);
+
+#ifndef perf_misc_flags
+#define perf_misc_flags(regs) (user_mode(regs) ? PERF_EVENT_MISC_USER : \
+ PERF_EVENT_MISC_KERNEL)
+#define perf_instruction_pointer(regs) instruction_pointer(regs)
+#endif
+
+#else
+static inline void
+perf_counter_task_sched_in(struct task_struct *task, int cpu) { }
+static inline void
+perf_counter_task_sched_out(struct task_struct *task,
+ struct task_struct *next, int cpu) { }
+static inline void
+perf_counter_task_tick(struct task_struct *task, int cpu) { }
+static inline int perf_counter_init_task(struct task_struct *child) { return 0; }
+static inline void perf_counter_exit_task(struct task_struct *child) { }
+static inline void perf_counter_free_task(struct task_struct *task) { }
+static inline void perf_counter_do_pending(void) { }
+static inline void perf_counter_print_debug(void) { }
+static inline void perf_disable(void) { }
+static inline void perf_enable(void) { }
+static inline int perf_counter_task_disable(void) { return -EINVAL; }
+static inline int perf_counter_task_enable(void) { return -EINVAL; }
+
+static inline void
+perf_swcounter_event(u32 event, u64 nr, int nmi,
+ struct pt_regs *regs, u64 addr) { }
+
+static inline void perf_counter_mmap(struct vm_area_struct *vma) { }
+static inline void perf_counter_comm(struct task_struct *tsk) { }
+static inline void perf_counter_fork(struct task_struct *tsk) { }
+static inline void perf_counter_init(void) { }
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_PERF_COUNTER_H */
diff --git a/process.c b/process.c
deleted file mode 100644
index e0a6fa2..0000000
--- a/process.c
+++ /dev/null
@@ -1,444 +0,0 @@
-/* MemProf -- memory profiler and leak detector
- * Copyright 1999, 2000, 2001, Red Hat, Inc.
- * Copyright 2002, Kristian Rietveld
- *
- * Sysprof -- Sampling, systemwide CPU profiler
- * Copyright 2004-2005 Soeren Sandmann
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "process.h"
-#include "binfile.h"
-
-#define PAGE_SIZE (getpagesize())
-
-static GHashTable *processes_by_cmdline;
-static GHashTable *processes_by_pid;
-
-typedef struct Map Map;
-struct Map
-{
- char * filename;
- gulong start;
- gulong end;
- gulong offset;
-#if 0
- gboolean do_offset;
-#endif
-
- BinFile * bin_file;
-};
-
-struct Process
-{
- char *cmdline;
-
- GList *maps;
- GList *bad_pages;
-
- int pid;
-};
-
-static void
-initialize (void)
-{
- if (!processes_by_cmdline)
- {
- processes_by_cmdline = g_hash_table_new (g_str_hash, g_str_equal);
- processes_by_pid = g_hash_table_new (g_direct_hash, g_direct_equal);
- }
-}
-
-static GList *
-read_maps (int pid)
-{
- char *name = g_strdup_printf ("/proc/%d/maps", pid);
- char buffer[1024];
- FILE *in;
- GList *result = NULL;
-
- in = fopen (name, "r");
- if (!in)
- {
-#if 0
- g_print ("could not open %d: %s\n", pid, g_strerror (errno));
-#endif
- g_free (name);
- return NULL;
- }
-
- while (fgets (buffer, sizeof (buffer) - 1, in))
- {
- char file[256];
- int count;
- gulong start;
- gulong end;
- gulong offset;
-
-#if 0
- g_print ("buffer: %s\n", buffer);
-#endif
-
- count = sscanf (
- buffer, "%lx-%lx %*15s %lx %*x:%*x %*u %255s",
- &start, &end, &offset, file);
- if (count == 4)
- {
- Map *map;
-
- map = g_new (Map, 1);
-
- map->filename = g_strdup (file);
- map->start = start;
- map->end = end;
-
- map->offset = offset;
-
- map->bin_file = NULL;
-
- result = g_list_prepend (result, map);
- }
-#if 0
- else
- {
- g_print ("scanf\n");
- }
-#endif
- }
-
- g_free (name);
- fclose (in);
- return result;
-}
-
-
-static Process *
-create_process (const char *cmdline, int pid)
-{
- Process *p;
-
- p = g_new (Process, 1);
-
- if (*cmdline != '\0')
- p->cmdline = g_strdup_printf ("[%s]", cmdline);
- else
- p->cmdline = g_strdup_printf ("[pid %d]", pid);
-
- p->bad_pages = NULL;
- p->maps = NULL;
- p->pid = 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_hash_table_insert (processes_by_pid, GINT_TO_POINTER (pid), p);
- g_hash_table_insert (processes_by_cmdline, g_strdup (cmdline), p);
-
- return p;
-}
-
-static Map *
-process_locate_map (Process *process, gulong addr)
-{
- GList *list;
-
- for (list = process->maps; list != NULL; list = list->next)
- {
- Map *map = list->data;
-
- if ((addr >= map->start) &&
- (addr < map->end))
- {
- return map;
- }
- }
-
- return NULL;
-}
-
-static void
-process_free_maps (Process *process)
-{
- GList *list;
-
- for (list = process->maps; list != NULL; list = list->next)
- {
- Map *map = list->data;
-
- if (map->filename)
- g_free (map->filename);
-
- if (map->bin_file)
- bin_file_free (map->bin_file);
-
- g_free (map);
- }
-
- g_list_free (process->maps);
-}
-
-static gboolean
-process_has_page (Process *process, gulong addr)
-{
- if (process_locate_map (process, addr))
- return TRUE;
- else
- return FALSE;
-}
-
-void
-process_ensure_map (Process *process, int pid, gulong addr)
-{
- /* Round down to closest page */
-
- addr = (addr - addr % PAGE_SIZE);
-
- if (process_has_page (process, addr))
- return;
-
- if (g_list_find (process->bad_pages, (gpointer)addr))
- return;
-
- /* a map containing addr was not found */
- if (process->maps)
- process_free_maps (process);
-
- process->maps = read_maps (pid);
-
- if (!process_has_page (process, addr))
- {
-#if 0
- g_print ("Bad page: %p\n", addr);
-#endif
- process->bad_pages = g_list_prepend (process->bad_pages, (gpointer)addr);
- }
-}
-
-static gboolean
-do_idle_free (gpointer d)
-{
- g_free (d);
- return FALSE;
-}
-
-static char *
-idle_free (char *d)
-{
- g_idle_add (do_idle_free, d);
- return d;
-}
-
-static char *
-get_cmdline (int pid)
-{
- char *cmdline;
- char *filename = idle_free (g_strdup_printf ("/proc/%d/cmdline", pid));
-
- if (g_file_get_contents (filename, &cmdline, NULL, NULL))
- {
- if (*cmdline == '\0')
- {
- g_free (cmdline);
- return NULL;
- }
- return cmdline;
- }
-
- return NULL;
-}
-
-static char *
-get_statname (int pid)
-{
- char *stat;
- char *filename = idle_free (g_strdup_printf ("/proc/%d/stat", pid));
-
-#if 0
- g_print ("stat %d\n", pid);
-#endif
-
- if (g_file_get_contents (filename, &stat, NULL, NULL))
- {
- char result[200];
-
- idle_free (stat);
-
- if (sscanf (stat, "%*d %200s %*s", result) == 1)
- return g_strndup (result, 200);
- }
-#if 0
- g_print ("return null\n");
-#endif
-
- return NULL;
-}
-
-static char *
-get_pidname (int pid)
-{
- if (pid == -1)
- return g_strdup_printf ("kernel");
- else
- return g_strdup_printf ("pid %d", pid);
-}
-
-static char *
-get_name (int pid)
-{
- char *cmdline = NULL;
-
- if ((cmdline = get_cmdline (pid)))
- return cmdline;
-
- if ((cmdline = get_statname (pid)))
- return cmdline;
-
- return get_pidname (pid);
-}
-
-Process *
-process_get_from_pid (int pid)
-{
- Process *p;
- gchar *cmdline = NULL;
-
- initialize();
-
- p = g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid));
-
- if (p)
- return p;
-
- cmdline = get_name (pid);
-
- p = g_hash_table_lookup (processes_by_cmdline, cmdline);
- if (p)
- return p;
- else
- return create_process (idle_free (cmdline), pid);
-}
-
-const Symbol *
-process_lookup_symbol (Process *process, gulong address)
-{
- static Symbol undefined;
- const Symbol *result;
- static Symbol kernel;
- Map *map = process_locate_map (process, address);
-
-/* g_print ("addr: %x\n", address); */
-
- if (address == 0x1)
- {
- kernel.name = "in kernel";
- kernel.address = 0x0001337;
- return &kernel;
- }
- else if (!map)
- {
- if (undefined.name)
- g_free (undefined.name);
- undefined.name = g_strdup_printf ("??? %s", process->cmdline);
- undefined.address = 0xBABE0001;
-
-#if 0
- g_print ("no map for %p (%s)\n", address, process->cmdline);
-#endif
- return &undefined;
- }
-
-#if 0
- g_print ("has map: %s\n", process->cmdline);
-#endif
-
-#if 0
- g_print ("has map: %s\n", process->cmdline);
-#endif
-
-/* if (map->do_offset) */
-/* address -= map->start; */
-
-#if 0
- /* convert address to file coordinates */
- g_print ("looking up %p ", address);
-#endif
-
- address -= map->start;
- address += map->offset;
-
- if (!map->bin_file)
- map->bin_file = bin_file_new (map->filename);
-
-/* g_print ("%s: start: %p, load: %p\n", */
-/* map->filename, map->start, bin_file_get_load_address (map->bin_file)); */
-
- result = bin_file_lookup_symbol (map->bin_file, address);
-
-#if 0
- g_print (" ---> %s\n", result->name);
-#endif
-
-/* g_print ("(%x) %x %x name; %s\n", address, map->start, map->offset, result->name); */
-
- return result;
-}
-
-const char *
-process_get_cmdline (Process *process)
-{
- 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;
-}
diff --git a/process.h b/process.h
deleted file mode 100644
index 6ad1eba..0000000
--- a/process.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* MemProf -- memory profiler and leak detector
- * Copyright 1999, 2000, 2001, Red Hat, Inc.
- * Copyright 2002, Kristian Rietveld
- *
- * Sysprof -- Sampling, systemwide CPU profiler
- * Copyright 2004, Red Hat, Inc
- * Copyright 2004, 2005, Soeren Sandmann
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef PROCESS_H
-#define PROCESS_H
-
-#include "binfile.h"
-
-typedef struct Process Process;
-
-/* We are making the assumption that pid's are not recycled during
- * a profiling run. That is wrong, but necessary to avoid reading
- * from /proc/<pid>/maps on every sample (which would be a race
- * condition anyway).
- *
- * If the address passed to new_from_pid() is somewhere that hasn't
- * been checked before, the mappings are reread for the Process. This
- * means that if some previously sampled pages have been unmapped,
- * they will be lost and appear as "???" on the profile.
- *
- * To flush the pid cache, call process_flush_caches().
- * This will invalidate all instances of Process.
- *
- */
-
-void process_flush_caches (void);
-Process * process_get_from_pid (int pid);
-void process_ensure_map (Process *process,
- int pid,
- gulong address);
-const Symbol *process_lookup_symbol (Process *process,
- gulong address);
-const Symbol *process_lookup_symbol_with_filename (Process *process,
- int pid,
- gulong map_start,
- const char *filename,
- gulong address);
-const char * process_get_cmdline (Process *process);
-
-
-#endif
diff --git a/profile.c b/profile.c
index f3dd0e9..51f328f 100644
--- a/profile.c
+++ b/profile.c
@@ -1,6 +1,6 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
- * Copyright 2004, 2005, Soeren Sandmann
+ * Copyright 2004, 2005, 2006, Soeren Sandmann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,120 +22,75 @@
#include <string.h>
#include "binfile.h"
-#include "process.h"
#include "stackstash.h"
#include "profile.h"
#include "sfile.h"
-typedef struct Node Node;
-
-static void
-update()
-{
-#if 0
- while (g_main_pending())
- g_main_iteration (FALSE);
-#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
{
- gint size;
- Node * call_tree;
-
- /* This table is really a cache. We can build it from the call_tree */
- GHashTable * nodes_by_object;
+ StackStash *stash;
};
static SFormat *
create_format (void)
{
- SType object_type = 0;
- SType node_type = 0;
-
- return sformat_new (
- sformat_new_record (
- "profile", NULL,
- sformat_new_integer ("size"),
- sformat_new_pointer ("call_tree", &node_type),
- sformat_new_list (
- "objects", NULL,
- sformat_new_record (
- "object", &object_type,
- sformat_new_string ("name"),
- sformat_new_integer ("total"),
- sformat_new_integer ("self"),
+ SFormat *format;
+ SForward *object_forward;
+ SForward *node_forward;
+
+ format = sformat_new();
+
+ object_forward = sformat_declare_forward (format);
+ node_forward = sformat_declare_forward (format);
+
+ sformat_set_type (
+ format,
+ sformat_make_record (
+ format, "profile", NULL,
+ sformat_make_integer (format, "size"),
+ sformat_make_pointer (format, "call_tree", node_forward),
+ sformat_make_list (
+ format, "objects", NULL,
+ sformat_make_record (
+ format, "object", object_forward,
+ sformat_make_string (format, "name"),
+ sformat_make_integer (format, "total"),
+ sformat_make_integer (format, "self"),
NULL)),
- sformat_new_list (
- "nodes", NULL,
- sformat_new_record (
- "node", &node_type,
- sformat_new_pointer ("object", &object_type),
- sformat_new_pointer ("siblings", &node_type),
- sformat_new_pointer ("children", &node_type),
- sformat_new_pointer ("parent", &node_type),
- sformat_new_integer ("total"),
- sformat_new_integer ("self"),
- sformat_new_integer ("toplevel"),
+ sformat_make_list (
+ format, "nodes", NULL,
+ sformat_make_record (
+ format, "node", node_forward,
+ sformat_make_pointer (format, "object", object_forward),
+ sformat_make_pointer (format, "siblings", node_forward),
+ sformat_make_pointer (format, "children", node_forward),
+ sformat_make_pointer (format, "parent", node_forward),
+ sformat_make_integer (format, "total"),
+ sformat_make_integer (format, "self"),
+ sformat_make_integer (format, "toplevel"),
NULL)),
NULL));
-}
-
-static void
-add_object (gpointer key, gpointer value, gpointer data)
-{
- SFileOutput *output = data;
- ProfileObject *object = key;
-
- 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);
+ return format;
}
static void
-serialize_call_tree (Node *node, SFileOutput *output)
+serialize_call_tree (StackNode *node,
+ SFileOutput *output)
{
if (!node)
return;
sfile_begin_add_record (output, "node");
- sfile_add_pointer (output, "object", node->object);
+ sfile_add_pointer (output, "object", U64_TO_POINTER (node->data));
sfile_add_pointer (output, "siblings", node->siblings);
sfile_add_pointer (output, "children", node->children);
sfile_add_pointer (output, "parent", node->parent);
sfile_add_integer (output, "total", node->total);
- sfile_add_integer (output, "self", node->self);
+ sfile_add_integer (output, "self", node->size);
sfile_add_integer (output, "toplevel", node->toplevel);
sfile_end_add (output, "node", node);
-
+
serialize_call_tree (node->siblings, output);
serialize_call_tree (node->children, output);
}
@@ -147,47 +102,49 @@ profile_save (Profile *profile,
{
gboolean result;
+ GList *profile_objects;
+ GList *list;
+
SFormat *format = create_format ();
SFileOutput *output = sfile_output_new (format);
-
+
sfile_begin_add_record (output, "profile");
-
- sfile_add_integer (output, "size", profile->size);
- sfile_add_pointer (output, "call_tree", profile->call_tree);
+ sfile_add_integer (output, "size", profile_get_size (profile));
+ sfile_add_pointer (output, "call_tree",
+ stack_stash_get_root (profile->stash));
+
+ profile_objects = profile_get_objects (profile);
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_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, "profile", NULL);
-
+
result = sfile_output_save (output, file_name, err);
-
- sformat_free (format);
- sfile_output_free (output);
-
- return result;
-}
-
-static void
-make_hash_table (Node *node, GHashTable *table)
-{
- if (!node)
- return;
-
- g_assert (node->object);
- node->next = g_hash_table_lookup (table, node->object);
- g_hash_table_insert (table, node->object, node);
-
- g_assert (node->siblings != (void *)0x11);
+ sfile_output_free (output);
+ sformat_free (format);
- make_hash_table (node->siblings, table);
- make_hash_table (node->children, table);
+ return result;
}
Profile *
@@ -197,398 +154,122 @@ profile_load (const char *filename, GError **err)
SFileInput *input;
Profile *profile;
int n, i;
+ StackNode *root;
format = create_format ();
input = sfile_load (filename, format, err);
-
+
if (!input)
return NULL;
profile = g_new (Profile, 1);
-
- profile->nodes_by_object =
- g_hash_table_new (direct_hash_no_null, g_direct_equal);
sfile_begin_get_record (input, "profile");
-
- sfile_get_integer (input, "size", &profile->size);
- sfile_get_pointer (input, "call_tree", (void **)&profile->call_tree);
-
+
+ sfile_get_integer (input, "size", NULL);
+ sfile_get_pointer (input, "call_tree", (gpointer *)&root);
+
n = sfile_begin_get_list (input, "objects");
for (i = 0; i < n; ++i)
{
- ProfileObject *obj = g_new (ProfileObject, 1);
+ char *string;
sfile_begin_get_record (input, "object");
-
- sfile_get_string (input, "name", &obj->name);
- sfile_get_integer (input, "total", (gint32 *)&obj->total);
- sfile_get_integer (input, "self", (gint32 *)&obj->self);
- sfile_end_get (input, "object", obj);
+ sfile_get_string (input, "name", &string);
+ sfile_get_integer (input, "total", NULL);
+ sfile_get_integer (input, "self", NULL);
+
+ sfile_end_get (input, "object", string);
}
sfile_end_get (input, "objects", NULL);
- profile->call_tree = NULL;
+ profile->stash = stack_stash_new ((GDestroyNotify)g_free);
+
n = sfile_begin_get_list (input, "nodes");
for (i = 0; i < n; ++i)
{
- Node *node = g_new (Node, 1);
-
+ StackNode *node = stack_node_new (profile->stash);
+ gint32 size;
+ gint32 total;
+
sfile_begin_get_record (input, "node");
-
- sfile_get_pointer (input, "object", (gpointer *)&node->object);
+
+ sfile_get_pointer (input, "object", (gpointer *)&node->data);
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
sfile_get_pointer (input, "children", (gpointer *)&node->children);
sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
- sfile_get_integer (input, "total", (gint32 *)&node->total);
- sfile_get_integer (input, "self", (gint32 *)&node->self);
- sfile_get_integer (input, "toplevel", &node->toplevel);
+ sfile_get_integer (input, "total", &total);
+ sfile_get_integer (input, "self", (gint32 *)&size);
+ sfile_get_integer (input, "toplevel", NULL);
+
+ node->total = total;
+ node->size = size;
sfile_end_get (input, "node", node);
-
+
g_assert (node->siblings != (void *)0x11);
}
sfile_end_get (input, "nodes", NULL);
sfile_end_get (input, "profile", NULL);
- sformat_free (format);
sfile_input_free (input);
-
- make_hash_table (profile->call_tree, profile->nodes_by_object);
-
- 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);
-}
+ sformat_free (format);
-static void
-compute_object_total (gpointer key, gpointer value, gpointer data)
-{
- Node *node;
- ProfileObject *object = key;
+ stack_stash_set_root (profile->stash, root);
- for (node = value; node != NULL; node = node->next)
- {
- object->self += node->self;
- if (node->toplevel)
- object->total += node->total;
- }
+ return profile;
}
Profile *
profile_new (StackStash *stash)
{
- Info info;
-
- info.profile = g_new (Profile, 1);
- 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 */
- 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);
+ Profile *profile = g_new (Profile, 1);
- g_hash_table_foreach (info.profile->nodes_by_object, compute_object_total, NULL);
+ profile->stash = stack_stash_ref (stash);
- g_hash_table_destroy (info.profile_objects);
-
- return info.profile;
+ return profile;
}
static void
-add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
+add_trace_to_tree (StackLink *trace, gint size, gpointer data)
{
- GList *list;
- GPtrArray *nodes_to_unmark = g_ptr_array_new ();
- GPtrArray *nodes_to_unmark_recursive = g_ptr_array_new ();
+ StackLink *link;
ProfileDescendant *parent = NULL;
- int i, len;
-
- GPtrArray *seen_objects = g_ptr_array_new ();
-
- for (list = trace; list != NULL; list = list->next)
+ ProfileDescendant **tree = data;
+
+ link = trace;
+ while (link->next)
+ link = link->next;
+
+ for (; link != NULL; link = link->prev)
{
- Node *node = list->data;
+ gpointer address = U64_TO_POINTER (link->data);
+ ProfileDescendant *prev = NULL;
ProfileDescendant *match = NULL;
-
- update();
-
- for (match = *tree; match != NULL; match = match->siblings)
+
+ for (match = *tree; match != NULL; prev = match, match = match->siblings)
{
- if (match->object == node->object)
+ if (match->name == address)
+ {
+ if (prev)
+ {
+ /* Move to front */
+ prev->siblings = match->siblings;
+ match->siblings = *tree;
+ *tree = match;
+ }
break;
+ }
}
if (!match)
{
- ProfileDescendant *seen_tree_node;
-
/* Have we seen this object further up the tree? */
- seen_tree_node = NULL;
- for (i = 0; i < seen_objects->len; ++i)
+ for (match = parent; match != NULL; match = match->parent)
{
- ProfileDescendant *n = seen_objects->pdata[i];
- if (n->object == node->object)
- seen_tree_node = n;
- }
-
- if (seen_tree_node)
- {
- ProfileDescendant *node;
-
- g_assert (parent);
-
- for (node = parent; node != seen_tree_node->parent; node = node->parent)
- {
- node->non_recursion -= size;
- --node->marked_non_recursive;
- }
-
- match = seen_tree_node;
-
- g_ptr_array_remove_range (seen_objects, 0, seen_objects->len);
- for (node = match; node != NULL; node = node->parent)
- g_ptr_array_add (seen_objects, node);
+ if (match->name == address)
+ break;
}
}
@@ -596,114 +277,40 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
{
match = g_new (ProfileDescendant, 1);
- match->object = node->object;
- match->non_recursion = 0;
- match->total = 0;
+ match->name = address;
+ match->cumulative = 0;
match->self = 0;
match->children = NULL;
- match->marked_non_recursive = 0;
- match->marked_total = FALSE;
match->parent = parent;
-
match->siblings = *tree;
*tree = match;
}
- if (!match->marked_non_recursive)
- {
- g_ptr_array_add (nodes_to_unmark, match);
- match->non_recursion += size;
- ++match->marked_non_recursive;
- }
-
- if (!match->marked_total)
- {
- g_ptr_array_add (nodes_to_unmark_recursive, match);
-
- match->total += size;
- match->marked_total = TRUE;
- }
-
- if (!list->next)
- match->self += size;
-
- g_ptr_array_add (seen_objects, match);
-
tree = &(match->children);
parent = match;
}
- g_ptr_array_free (seen_objects, TRUE);
-
- len = nodes_to_unmark->len;
- for (i = 0; i < len; ++i)
+ parent->self += size;
+ while (parent)
{
- ProfileDescendant *tree_node = nodes_to_unmark->pdata[i];
-
- tree_node->marked_non_recursive = 0;
+ parent->cumulative += size;
+ parent = parent->parent;
}
-
- len = nodes_to_unmark_recursive->len;
- for (i = 0; i < len; ++i)
- {
- ProfileDescendant *tree_node = nodes_to_unmark_recursive->pdata[i];
-
- tree_node->marked_total = FALSE;
- }
-
- g_ptr_array_free (nodes_to_unmark, TRUE);
- g_ptr_array_free (nodes_to_unmark_recursive, TRUE);
-}
-
-static void
-node_list_leaves (Node *node, GList **leaves)
-{
- 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;
- Node *node;
-
- for (node = leaf; node != top->parent; node = node->parent)
- trace = g_list_prepend (trace, node);
-
- add_trace_to_tree (tree, trace, leaf->self);
-
- g_list_free (trace);
}
ProfileDescendant *
-profile_create_descendants (Profile *profile, ProfileObject *object)
+profile_create_descendants (Profile *profile,
+ char *object_name)
{
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)
{
- update();
if (node->toplevel)
- {
- GList *leaves = NULL;
- GList *list;
-
- node_list_leaves (node, &leaves);
-
- for (list = leaves; list != NULL; list = list->next)
- add_leaf_to_tree (&tree, list->data, node);
-
- g_list_free (leaves);
- }
+ stack_node_foreach_trace (node, add_trace_to_tree, &tree);
+
node = node->next;
}
@@ -714,124 +321,212 @@ static ProfileCaller *
profile_caller_new (void)
{
ProfileCaller *caller = g_new (ProfileCaller, 1);
+
caller->next = NULL;
caller->self = 0;
caller->total = 0;
+
return caller;
}
ProfileCaller *
-profile_list_callers (Profile *profile,
- ProfileObject *callee)
+profile_list_callers (Profile *profile,
+ char *callee_name)
{
- Node *callee_node;
- Node *node;
- GHashTable *callers_by_object;
- GHashTable *seen_callers;
+ StackNode *node;
+ StackNode *callees;
+ GHashTable *callers_by_name;
+ GHashTable *processed_callers;
ProfileCaller *result = NULL;
+
+ callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal);
+ processed_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
- callers_by_object =
- 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);
-
- for (node = callee_node; node; node = node->next)
+ callees = stack_stash_find_node (profile->stash, callee_name);
+
+ for (node = callees; node != NULL; node = node->next)
{
- ProfileObject *object;
- if (node->parent)
- object = node->parent->object;
- else
- object = NULL;
+ ProfileCaller *caller;
- if (!g_hash_table_lookup (callers_by_object, object))
+ if (!node->parent)
+ continue;
+
+ caller = g_hash_table_lookup (
+ callers_by_name, U64_TO_POINTER (node->parent->data));
+
+ if (!caller)
{
- ProfileCaller *caller = profile_caller_new ();
- caller->object = object;
- g_hash_table_insert (callers_by_object, object, caller);
+ caller = profile_caller_new ();
+ caller->name = U64_TO_POINTER (node->parent->data);
+ caller->total = 0;
+ caller->self = 0;
caller->next = result;
result = caller;
+
+ g_hash_table_insert (
+ callers_by_name, U64_TO_POINTER (node->parent->data), caller);
}
}
- for (node = callee_node; node != NULL; node = node->next)
- {
- Node *top_caller;
- Node *top_callee;
- Node *n;
+ for (node = callees; node != NULL; node = node->next)
+ {
+ StackNode *top_caller = node->parent;
+ StackNode *top_callee = node;
+ StackNode *n;
ProfileCaller *caller;
- ProfileObject *object;
-
- if (node->parent)
- object = node->parent->object;
- else
- object = NULL;
-
- caller = g_hash_table_lookup (callers_by_object, object);
+
+ if (!node->parent)
+ continue;
- /* find topmost node/parent pair identical to this node/parent */
- top_caller = node->parent;
- top_callee = node;
for (n = node; n && n->parent; n = n->parent)
{
- if (n->object == node->object &&
- n->parent->object == node->parent->object)
+ if (n->data == node->data &&
+ n->parent->data == node->parent->data)
{
top_caller = n->parent;
top_callee = n;
}
}
+
+ caller = g_hash_table_lookup (
+ callers_by_name, U64_TO_POINTER (node->parent->data));
- if (!g_hash_table_lookup (seen_callers, top_caller))
+ if (!g_hash_table_lookup (processed_callers, top_caller))
{
caller->total += top_callee->total;
- g_hash_table_insert (seen_callers, top_caller, (void *)0x1);
+ g_hash_table_insert (processed_callers, top_caller, top_caller);
}
- if (node->self > 0)
- caller->self += node->self;
+ caller->self += node->size;
}
-
- g_hash_table_destroy (seen_callers);
- g_hash_table_destroy (callers_by_object);
-
+
+ g_hash_table_destroy (processed_callers);
+ g_hash_table_destroy (callers_by_name);
+
return result;
-
}
+#if 0
+/* This code generates a list of all ancestors, rather than
+ * all callers. It turned out to not work well in practice,
+ * but on the other hand the single list of callers is not
+ * all that great either, so we'll keep it around commented
+ * out for now
+ */
+
static void
-node_free (Node *node)
+add_to_list (gpointer key,
+ gpointer value,
+ gpointer user_data)
{
- if (!node)
- return;
+ GList **list = user_data;
+
+ *list = g_list_prepend (*list, value);
+}
+
+static GList *
+listify_hash_table (GHashTable *hash_table)
+{
+ GList *result = NULL;
- node_free (node->siblings);
- node_free (node->children);
- g_free (node);
+ g_hash_table_foreach (hash_table, add_to_list, &result);
+
+ return result;
}
-static void
-free_object (gpointer key, gpointer value, gpointer data)
+ProfileCaller *
+profile_list_ancestors (Profile *profile,
+ char *callee_name)
{
- profile_object_free (key);
+ StackNode *callees;
+ StackNode *node;
+ GHashTable *callers_by_name;
+ ProfileCaller *result = NULL;
+
+ callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ callees = stack_stash_find_node (profile->stash, callee_name);
+
+ for (node = callees; node != NULL; node = node->next)
+ {
+ StackNode *n;
+ gboolean seen_recursive_call;
+ GHashTable *total_ancestors;
+ GHashTable *all_ancestors;
+ GList *all, *list;
+
+ /* Build a list of those ancestors that should get assigned
+ * totals. If this callee does not have any recursive calls
+ * higher up, that means all of it's ancestors. If it does
+ * have a recursive call, only the one between this node
+ * and the recursive call should get assigned total
+ */
+ seen_recursive_call = FALSE;
+ all_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal);
+ total_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal);
+ for (n = node->parent; n; n = n->parent)
+ {
+ if (!seen_recursive_call)
+ {
+ g_hash_table_insert (total_ancestors, n->address, n);
+ }
+ else
+ {
+ g_hash_table_remove (total_ancestors, n->address);
+ }
+
+ g_hash_table_insert (all_ancestors, n->address, n);
+
+ if (n->address == node->address)
+ seen_recursive_call = TRUE;
+ }
+
+ all = listify_hash_table (all_ancestors);
+
+ for (list = all; list; list = list->next)
+ {
+ ProfileCaller *caller;
+ StackNode *ancestor = list->data;
+
+ caller = g_hash_table_lookup (callers_by_name, ancestor->address);
+
+ if (!caller)
+ {
+ caller = profile_caller_new ();
+ g_hash_table_insert (
+ callers_by_name, ancestor->address, caller);
+ caller->name = ancestor->address;
+
+ caller->next = result;
+ result = caller;
+ }
+
+ caller->self += node->size;
+
+ if (g_hash_table_lookup (total_ancestors, ancestor->address))
+ caller->total += node->total;
+ }
+
+ g_list_free (all);
+ g_hash_table_destroy (all_ancestors);
+ g_hash_table_destroy (total_ancestors);
+ }
+
+ return result;
}
+#endif
void
profile_free (Profile *profile)
{
- g_hash_table_foreach (profile->nodes_by_object, free_object, NULL);
-
- node_free (profile->call_tree);
-
- g_hash_table_destroy (profile->nodes_by_object);
-
+ stack_stash_unref (profile->stash);
g_free (profile);
}
void
-profile_descendant_free (ProfileDescendant *descendant)
+profile_descendant_free (ProfileDescendant *descendant)
{
if (!descendant)
return;
@@ -852,13 +547,38 @@ profile_caller_free (ProfileCaller *caller)
g_free (caller);
}
+static int
+compute_total (StackNode *node)
+{
+ StackNode *n;
+ int total = 0;
+
+ for (n = node; n != NULL; n = n->next)
+ {
+ if (n->toplevel)
+ total += n->total;
+ }
+
+ return total;
+}
+
static void
-build_object_list (gpointer key, gpointer value, gpointer data)
+build_object_list (StackNode *node, gpointer data)
{
- ProfileObject *object = key;
GList **objects = data;
+ ProfileObject *obj;
+ StackNode *n;
+
+ obj = g_new (ProfileObject, 1);
+ obj->name = U64_TO_POINTER (node->data);
+
+ obj->total = compute_total (node);
+ obj->self = 0;
+
+ for (n = node; n != NULL; n = n->next)
+ obj->self += n->size;
- *objects = g_list_prepend (*objects, object);
+ *objects = g_list_prepend (*objects, obj);
}
GList *
@@ -866,7 +586,8 @@ profile_get_objects (Profile *profile)
{
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);
return objects;
}
@@ -874,5 +595,11 @@ profile_get_objects (Profile *profile)
gint
profile_get_size (Profile *profile)
{
- return profile->size;
+ StackNode *n;
+ gint size = 0;
+
+ for (n = stack_stash_get_root (profile->stash); n != NULL; n = n->siblings)
+ size += n->total;
+
+ return size;
}
diff --git a/profile.h b/profile.h
index edc1c45..ed016da 100644
--- a/profile.h
+++ b/profile.h
@@ -17,9 +17,10 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef PROFILE_H
+#define PROFILE_H
+
#include <glib.h>
-#include "binfile.h"
-#include "process.h"
#include "stackstash.h"
typedef struct Profile Profile;
@@ -29,7 +30,10 @@ typedef struct ProfileCaller ProfileCaller;
struct ProfileObject
{
- char * name; /* identifies this object uniquely */
+ char * name; /* identifies this object uniquely
+ * (the pointer itself, not the
+ * string)
+ */
guint total; /* sum of all toplevel totals */
guint self; /* sum of all selfs */
@@ -37,21 +41,17 @@ struct ProfileObject
struct ProfileDescendant
{
- ProfileObject * object;
+ char * name;
guint self;
- guint total;
- guint non_recursion;
+ guint cumulative;
ProfileDescendant * parent;
ProfileDescendant * siblings;
ProfileDescendant * children;
-
- int marked_non_recursive;
- int marked_total;
};
struct ProfileCaller
{
- ProfileObject * object; /* can be NULL */
+ char * name;
guint total;
guint self;
@@ -63,9 +63,9 @@ void profile_free (Profile *profile);
gint profile_get_size (Profile *profile);
GList * profile_get_objects (Profile *profile);
ProfileDescendant *profile_create_descendants (Profile *prf,
- ProfileObject *object);
+ char *object);
ProfileCaller * profile_list_callers (Profile *profile,
- ProfileObject *callee);
+ char *object);
void profile_caller_free (ProfileCaller *caller);
void profile_descendant_free (ProfileDescendant *descendant);
@@ -74,3 +74,5 @@ gboolean profile_save (Profile *profile,
GError **err);
Profile * profile_load (const char *filename,
GError **err);
+
+#endif /* PROFILE_H */
diff --git a/sfile.c b/sfile.c
index 31a152c..a106595 100644
--- a/sfile.c
+++ b/sfile.c
@@ -1,3 +1,5 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
+
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
* Copyright 2004, 2005, Soeren Sandmann
@@ -17,8 +19,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
-
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -27,57 +27,68 @@
#include <bzlib.h>
#endif
#include "sfile.h"
+#include "sformat.h"
+#include <sys/mman.h>
-typedef struct State State;
-typedef struct Transition Transition;
-typedef struct Fragment Fragment;
-
-struct SFormat
-{
- State *begin;
- State *end;
-};
-
-enum
-{
- TYPE_UNDEFINED = 0,
- TYPE_POINTER,
- TYPE_STRING,
- TYPE_INTEGER,
- TYPE_GENERIC_RECORD,
- TYPE_GENERIC_LIST,
- TYPE_VOID,
- N_BUILTIN_TYPES,
-};
-
+typedef struct BuildContext BuildContext;
+typedef struct Instruction Instruction;
typedef enum
{
- BEGIN,
- VALUE,
- END
-} TransitionKind;
-
-struct Transition
-{
- SType type;
- TransitionKind kind;
- State *to;
- char *element; /* for begin/end transitions */
- SType target_type; /* for pointer transitions */
-};
+ BEGIN, END, VALUE
+} InstructionKind;
-struct State
+struct Instruction
{
- GQueue *transitions;
+ InstructionKind kind;
+ SType *type;
+
+ union
+ {
+ struct
+ {
+ int n_elements;
+ int id;
+ Instruction *end_instruction;
+ } begin;
+
+ struct
+ {
+ Instruction *begin_instruction;
+ gpointer object;
+ } end;
+
+ struct
+ {
+ int target_id;
+ Instruction *target_instruction;
+ gpointer target_object;
+ gpointer *location;
+ } pointer;
+
+ struct
+ {
+ int value;
+ } integer;
+
+ struct
+ {
+ char *value;
+ } string;
+ } u;
};
-struct Fragment
+struct BuildContext
{
- Transition *enter, *exit;
+ SContext *context;
+
+ GArray *instructions;
};
static void
-set_error (GError **err, gint code, const char *format, va_list args)
+set_error (GError **err,
+ gint code,
+ const char *format,
+ va_list args)
{
char *msg;
@@ -129,528 +140,30 @@ set_invalid_content_error (GError **err, const char *format, ...)
va_end (args);
}
-static Transition *
-transition_new (const char *element,
- TransitionKind kind,
- SType type,
- State *from,
- State *to)
-{
- Transition *t = g_new (Transition, 1);
-
- g_assert (element || kind == VALUE);
-
- t->element = element? g_strdup (element) : NULL;
- t->kind = kind;
- t->type = type;
- t->to = to;
- t->target_type = TYPE_UNDEFINED;
-
- if (from)
- g_queue_push_tail (from->transitions, t);
-
- return t;
-}
-
-static void
-transition_free (Transition *transition)
-{
- if (transition->element)
- g_free (transition->element);
- g_free (transition);
-}
-
-static State *
-state_new (void)
-{
- State *state = g_new (State, 1);
- state->transitions = g_queue_new ();
- return state;
-}
-
-static void
-state_free (State *state)
-{
- GList *list;
-
- for (list = state->transitions->head; list; list = list->next)
- {
- Transition *transition = list->data;
-
- transition_free (transition);
- }
-
- g_queue_free (state->transitions);
- g_free (state);
-}
-
-SFormat *
-sformat_new (gpointer f)
-{
- SFormat *sformat = g_new0 (SFormat, 1);
- Fragment *fragment = f;
-
- sformat->begin = state_new ();
- sformat->end = state_new ();
-
- g_queue_push_tail (sformat->begin->transitions, fragment->enter);
- fragment->exit->to = sformat->end;
-
- g_free (fragment);
-
- return sformat;
-}
-
-#if 0
-SFormat *
-sformat_new_optional (gpointer f)
-{
- SFormat *sformat = g_new0 (SFormat, 1);
- Fragment *fragment = f;
-
- sformat->begin = state_new ();
- sformat->end = state_new ();
-}
-#endif
-
-static void
-add_state (State *state, GHashTable *seen_states, GQueue *todo_list)
-{
- if (!g_hash_table_lookup (seen_states, state))
- {
- g_hash_table_insert (seen_states, state, state);
- g_queue_push_tail (todo_list, state);
- }
-}
-
-void
-sformat_free (SFormat *format)
-{
- GHashTable *seen_states = g_hash_table_new (g_direct_hash, g_direct_equal);
- GQueue *todo_list = g_queue_new ();
-
- add_state (format->begin, seen_states, todo_list);
- add_state (format->end, seen_states, todo_list);
-
- while (!g_queue_is_empty (todo_list))
- {
- GList *list;
- State *state = g_queue_pop_head (todo_list);
-
- for (list = state->transitions->head; list != NULL; list = list->next)
- {
- Transition *transition = list->data;
- add_state (transition->to, seen_states, todo_list);
- }
-
- state_free (state);
- }
-
- g_hash_table_destroy (seen_states);
- g_queue_free (todo_list);
-}
-
-static GQueue *
-fragment_queue (Fragment *fragment1, va_list args)
-{
- GQueue *fragments = g_queue_new ();
- Fragment *fragment;
-
- g_queue_push_tail (fragments, fragment1);
-
- fragment = va_arg (args, Fragment *);
- while (fragment)
- {
- g_queue_push_tail (fragments, fragment);
- fragment = va_arg (args, Fragment *);
- }
-
- return fragments;
-}
-
-#if 0
-/* Consider adding unions at some point
- *
- * To be useful they should probably be anonymous, so that
- * the union itself doesn't have a representation in the
- * xml file.
- *
- * API:
- * sformat_new_union (gpointer content1, ...);
- *
- * char *content = begin_get_union ();
- * if (strcmp (content, ...) == 0)
- * get_pointer ();
- * else if (strcmp (content, ...) == 0)
- *
- * ;
- *
- * Annoying though, that we then won't have the nice one-to-one
- * correspondence between begin()/end() calls and <element></element>s
- * Actually, we will probably have to have <union>asdlfkj</union>
- * elements. That will make things a lot easier, and unions are
- * still pretty useful if you put big things like lists in them.
- *
- * We may also consider adding anonymous records. These will
- * not be able to have pointers associated with them though
- * (because there wouldn't be a natural place
- *
- *
- * Also consider adding the following data types:
- *
- * Binary blobs of data, stored as base64 perhaps
- * floating point values. How do we store those portably
- * without losing precision? Gnumeric may know.
- * enums, stored as strings
- * booleans
- */
-gpointer
-sformat_new_union (const char *name,
- gpointer content1,
- ...)
-{
- va_list args;
- GQueue *fragments;
- GList *list;
- Fragment *fragment;
- Transition *enter, *exit;
- State *begin;
- State *end;
-
- va_start (args, content1);
-
- fragments = fragment_queue (args);
-
- va_end (args);
-
- begin = state_new ();
- end = state_new ();
-
- enter = transition_new (name, TRANSITION_BEGIN_UNION, NULL, begin);
- exit = transition_new (name, TRANSITION_END_UNION, end, NULL);
-
- for (list = fragments->head; list; list = list->next)
- {
- Fragment *fragment = list->data;
-
- g_queue_push_tail (begin->transitions, fragment->enter);
- }
-
- for (list = fragments->head; list; list = list->next)
- {
- fragment = list->data;
-
- fragment->exit->to = end;
-
- g_free (fragment);
- }
-
- g_queue_free (fragments);
-
- fragment = g_new (Fragment, 1);
- fragment->enter = enter;
- fragment->exit = exit;
-
- return fragment;
-}
-#endif
-
-#define RECORD_SHIFT (sizeof (SType) * 8 - 1)
-#define LIST_SHIFT (sizeof (SType) * 8 - 2)
-
-static SType
-define_type (SType *type, SType fallback)
-{
- static SType type_ids = N_BUILTIN_TYPES;
-
- if (type)
- {
- if (*type == 0)
- *type = type_ids++;
-
- return *type;
- }
-
- return fallback;
-}
-
-static gboolean
-is_record_type (SType type)
-{
- /* FIMXE - not10 */
- return TRUE;
-}
-
-static gboolean
-is_list_type (SType type)
-{
- /* FIXME - not10 */
- return TRUE;
-}
-
-gpointer
-sformat_new_record (const char * name,
- SType *type,
- gpointer content1,
- ...)
-{
- va_list args;
- GQueue *fragments;
- State *begin, *state;
- Fragment *fragment;
- GList *list;
- SType real_type;
-
- /* Build queue of fragments */
- va_start (args, content1);
-
- fragments = fragment_queue (content1, args);
-
- va_end (args);
-
- /* chain fragments together */
- state = begin = state_new ();
-
- for (list = fragments->head; list != NULL; list = list->next)
- {
- fragment = list->data;
-
- g_queue_push_tail (state->transitions, fragment->enter);
-
- state = state_new ();
- fragment->exit->to = state;
- }
-
- real_type = define_type (type, TYPE_GENERIC_RECORD);
-
- /* Return resulting fragment */
- fragment = g_new (Fragment, 1);
- fragment->enter = transition_new (name, BEGIN, real_type, NULL, begin);
- fragment->exit = transition_new (name, END, real_type, state, NULL);
-
- return fragment;
-}
-
-gpointer
-sformat_new_list (const char *name,
- SType *type,
- gpointer content)
-{
- Fragment *m = content;
- State *list_state;
- Transition *enter, *exit;
- SType real_type;
-
- list_state = state_new ();
-
- real_type = define_type (type, TYPE_GENERIC_LIST);
-
- enter = transition_new (name, BEGIN, real_type, NULL, list_state);
- exit = transition_new (name, END, real_type, list_state, NULL);
-
- g_queue_push_tail (list_state->transitions, m->enter);
- m->exit->to = list_state;
-
- m->enter = enter;
- m->exit = exit;
-
- return m;
-}
-
-static gpointer
-sformat_new_value (const char *name,
- SType type)
-{
- Fragment *m = g_new (Fragment, 1);
- State *before, *after;
- Transition *value;
-
- before = state_new ();
- after = state_new ();
-
- m->enter = transition_new (name, BEGIN, type, NULL, before);
- m->exit = transition_new (name, END, type, after, NULL);
- value = transition_new (NULL, VALUE, type, before, after);
-
- return m;
-}
-
-gpointer
-sformat_new_pointer (const char *name,
- SType *target_type)
-{
- Fragment *fragment = sformat_new_value (name, TYPE_POINTER);
- Transition *value;
-
- /* store the target type in the value transition */
- value = fragment->enter->to->transitions->head->data;
- value->target_type = define_type (target_type, TYPE_VOID);
-
- return fragment;
-}
-
-gpointer
-sformat_new_integer (const char *name)
-{
- return sformat_new_value (name, TYPE_INTEGER);
-}
-
-gpointer
-sformat_new_string (const char *name)
-{
- return sformat_new_value (name, TYPE_STRING);
-}
-
-static const State *
-sformat_get_start_state (SFormat *format)
-{
- return format->begin;
-}
-
+/* reading */
static gboolean
-sformat_is_end_state (SFormat *format, const State *state)
-{
- return format->end == state;
-}
-
-static const State *
-state_transition_check (const State *state,
- const char *element,
- TransitionKind kind,
- SType *type)
-{
- GList *list;
-
- for (list = state->transitions->head; list; list = list->next)
- {
- Transition *transition = list->data;
-
- if (transition->kind == kind &&
- strcmp (element, transition->element) == 0)
- {
- *type = transition->type;
- return transition->to;
- }
- }
-
- return NULL;
-}
-
-static const State *
-state_transition_begin (const State *state, const char *element, SType *type)
-{
- return state_transition_check (state, element, BEGIN, type);
-}
-
-static const State *
-state_transition_end (const State *state, const char *element, SType *type)
-{
- return state_transition_check (state, element, END, type);
-}
-
-static const State *
-state_transition_text (const State *state, SType *type, SType *target_type)
+is_all_blank (const char *text)
{
- GList *list;
-
- for (list = state->transitions->head; list; list = list->next)
- {
- Transition *transition = list->data;
-
- if (transition->kind == VALUE)
- {
- *type = transition->type;
-
- if (*type == TYPE_POINTER && target_type)
- *target_type = transition->target_type;
-
- /* There will never be more than one allowed value transition for
- * a given state
- */
- return transition->to;
- }
-#if 0
- else
- g_print ("transition: %d (%s)\n", transition->kind, transition->element);
-#endif
- }
+ while (g_ascii_isspace (*text))
+ text++;
- return NULL;
+ return (*text == '\0');
}
-/* reading */
-typedef struct BuildContext BuildContext;
-typedef struct Instruction Instruction;
-
-struct Instruction
-{
- TransitionKind kind;
- SType type;
-
- char *name;
- union
- {
- struct
- {
- gboolean is_list;
- gboolean is_record;
- int n_elements;
- int id;
- Instruction *end_instruction;
- } begin;
-
- struct
- {
- Instruction *begin_instruction;
- gpointer object;
- } end;
-
- struct
- {
- SType target_type;
- int target_id;
- Instruction *target_instruction;
- gpointer target_object;
- gpointer *location;
- } pointer;
-
- struct
- {
- int value;
- } integer;
-
- struct
- {
- char *value;
- } string;
- } u;
-};
-
-struct BuildContext
-{
- const State *state;
-
- GArray *instructions;
-};
-
static gboolean
get_number (const char *text, int *number)
{
char *end;
int result;
- char *stripped;
gboolean retval;
-
- stripped = g_strstrip (g_strdup (text));
- result = strtol (stripped, &end, 10);
-
- retval = (*end == '\0');
-
+
+ result = strtol (text, &end, 10);
+
+ retval = is_all_blank (end);
+
if (retval && number)
*number = result;
- g_free (stripped);
-
return retval;
}
@@ -662,14 +175,23 @@ struct SFileInput
GHashTable *instructions_by_location;
};
+static gboolean
+check_name (Instruction *instr,
+ const char *name)
+{
+ const char *type_name = stype_get_name (instr->type);
+
+ return strcmp (name, type_name) == 0;
+}
+
void
sfile_begin_get_record (SFileInput *file, const char *name)
{
Instruction *instruction = file->current_instruction++;
-
+
g_return_if_fail (instruction->kind == BEGIN);
- g_return_if_fail (strcmp (instruction->name, name) == 0);
- g_return_if_fail (is_record_type (instruction->type));
+ g_return_if_fail (check_name (instruction, name));
+ g_return_if_fail (stype_is_record (instruction->type));
}
int
@@ -679,8 +201,8 @@ sfile_begin_get_list (SFileInput *file,
Instruction *instruction = file->current_instruction++;
g_return_val_if_fail (instruction->kind == BEGIN, 0);
- g_return_val_if_fail (strcmp (instruction->name, name) == 0, 0);
- g_return_val_if_fail (is_list_type (instruction->type), 0);
+ g_return_val_if_fail (check_name (instruction, name), 0);
+ g_return_val_if_fail (stype_is_list (instruction->type), 0);
return instruction->u.begin.n_elements;
}
@@ -691,105 +213,72 @@ sfile_get_pointer (SFileInput *file,
gpointer *location)
{
Instruction *instruction;
-
- instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_POINTER &&
- strcmp (instruction->name, name) == 0);
instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_POINTER);
+ g_return_if_fail (stype_is_pointer (instruction->type));
+
+ instruction->u.pointer.location = location;
- instruction->u.pointer.location = location;
-
*location = (gpointer) 0xFedeAbe;
-
+
if (location)
{
if (g_hash_table_lookup (file->instructions_by_location, location))
g_warning ("Reading into the same location twice\n");
-
+
g_hash_table_insert (file->instructions_by_location, location, instruction);
}
-
- instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_POINTER &&
- strcmp (instruction->name, name) == 0);
-
-}
+ }
void
-sfile_get_integer (SFileInput *file,
- const char *name,
- gint32 *integer)
+sfile_get_integer (SFileInput *file,
+ const char *name,
+ gint32 *integer)
{
Instruction *instruction;
instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_INTEGER &&
- strcmp (instruction->name, name) == 0);
-
- instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_INTEGER);
+ g_return_if_fail (stype_is_integer (instruction->type));
if (integer)
*integer = instruction->u.integer.value;
-
- instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_INTEGER &&
- strcmp (instruction->name, name) == 0);
}
void
-sfile_get_string (SFileInput *file,
- const char *name,
- char **string)
+sfile_get_string (SFileInput *file,
+ const char *name,
+ char **string)
{
Instruction *instruction;
-
- instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_STRING &&
- strcmp (instruction->name, name) == 0);
instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_STRING);
+ g_return_if_fail (stype_is_string (instruction->type));
if (string)
*string = g_strdup (instruction->u.string.value);
-
- instruction = file->current_instruction++;
- g_return_if_fail (instruction->type == TYPE_STRING &&
- strcmp (instruction->name, name) == 0);
}
static void
hook_up_pointers (SFileInput *file)
{
int i;
-
-#if 0
- g_print ("emfle\n");
-#endif
+
for (i = 0; i < file->n_instructions; ++i)
{
Instruction *instruction = &(file->instructions[i]);
-
- if (instruction->kind == VALUE &&
- instruction->type == TYPE_POINTER)
+
+ if (stype_is_pointer (instruction->type))
{
gpointer target_object;
Instruction *target_instruction;
-
+
target_instruction = instruction->u.pointer.target_instruction;
-
+
if (target_instruction)
target_object = target_instruction->u.begin.end_instruction->u.end.object;
else
target_object = NULL;
-
-#if 0
- g_print ("target object: %p\n", target_object);
-#endif
-
+
*(instruction->u.pointer.location) = target_object;
}
}
@@ -803,10 +292,10 @@ sfile_end_get (SFileInput *file,
Instruction *instruction = file->current_instruction++;
g_return_if_fail (instruction->kind == END);
- g_return_if_fail (strcmp (instruction->name, name) == 0);
-
+ g_return_if_fail (check_name (instruction, name));
+
instruction->u.end.object = object;
-
+
if (file->current_instruction == file->instructions + file->n_instructions)
hook_up_pointers (file);
}
@@ -824,13 +313,13 @@ get_id (const char **names, const char **values, GError **err)
set_unknown_attribute_error (err, "Unknown attribute: %s", names[i]);
return -1;
}
-
+
if (id_string)
{
set_invalid_content_error (err, "Attribute 'id' defined twice");
return -1;
}
-
+
id_string = values[i];
}
@@ -856,23 +345,24 @@ handle_begin_element (GMarkupParseContext *parse_context,
{
BuildContext *build = user_data;
Instruction instruction;
-
+
instruction.u.begin.id = get_id (attribute_names, attribute_values, err);
-
+
if (instruction.u.begin.id == -1)
return;
-
- build->state = state_transition_begin (build->state, element_name, &instruction.type);
- if (!build->state)
+
+ instruction.type = scontext_begin (build->context, element_name);
+ if (!instruction.type)
{
set_unknown_element_error (err, "<%s> unexpected here", element_name);
return;
}
- /* FIXME - not10: is there really a reason to add begin/end instructions for values? */
- instruction.name = g_strdup (element_name);
- instruction.kind = BEGIN;
- g_array_append_val (build->instructions, instruction);
+ if (stype_is_list (instruction.type) || stype_is_record (instruction.type))
+ {
+ instruction.kind = BEGIN;
+ g_array_append_val (build->instructions, instruction);
+ }
}
static void
@@ -884,36 +374,47 @@ handle_end_element (GMarkupParseContext *context,
BuildContext *build = user_data;
Instruction instruction;
- build->state = state_transition_end (build->state, element_name, &instruction.type);
- if (!build->state)
+ instruction.type = scontext_end (build->context, element_name);
+ if (!instruction.type)
{
set_unknown_element_error (err, "</%s> unexpected here", element_name);
return;
}
-
- instruction.name = g_strdup (element_name);
- instruction.kind = END;
-
- g_array_append_val (build->instructions, instruction);
+
+ if (stype_is_list (instruction.type) || stype_is_record (instruction.type))
+ {
+ instruction.kind = END;
+
+ g_array_append_val (build->instructions, instruction);
+ }
}
static gboolean
decode_text (const char *text, char **decoded)
{
int length = strlen (text);
-
+
if (length < 2)
return FALSE;
-
+
if (text[0] != '\"' || text[length - 1] != '\"')
return FALSE;
-
+
if (decoded)
*decoded = g_strndup (text + 1, length - 2);
return TRUE;
}
+static const char *
+skip_whitespace (const char *text)
+{
+ while (g_ascii_isspace (*text))
+ text++;
+
+ return text;
+}
+
static void
handle_text (GMarkupParseContext *context,
const gchar *text,
@@ -923,80 +424,68 @@ handle_text (GMarkupParseContext *context,
{
BuildContext *build = user_data;
Instruction instruction;
- char *free_me;
- SType target_type;
-
- text = free_me = g_strstrip (g_strdup (text));
-
- if (strlen (text) == 0)
- goto out;
-
- build->state = state_transition_text (build->state, &instruction.type, &target_type);
- if (!build->state)
+
+ if (*text == '\0')
+ return;
+
+ text = skip_whitespace (text);
+ if (*text == '\0')
+ return;
+
+ instruction.type = scontext_text (build->context);
+ if (!instruction.type)
{
+ /* FIXME: what are line and ch used for here? */
int line, ch;
g_markup_parse_context_get_position (context, &line, &ch);
-#if 0
- g_print ("line: %d char: %d\n", line, ch);
-#endif
set_invalid_content_error (err, "Unexpected text data");
- goto out;
+ return;
}
-
- instruction.name = NULL;
+
instruction.kind = VALUE;
- switch (instruction.type)
+ if (stype_is_pointer (instruction.type))
{
- case TYPE_POINTER:
- instruction.u.pointer.target_type = target_type;
if (!get_number (text, &instruction.u.pointer.target_id))
{
- set_invalid_content_error (err, "Contents '%s' of pointer element is not a number", text);
- goto out;
+ set_invalid_content_error (err, "Content '%s' of pointer element is not a number", text);
+ return;
}
- break;
-
- case TYPE_INTEGER:
+ }
+ else if (stype_is_integer (instruction.type))
+ {
if (!get_number (text, &instruction.u.integer.value))
{
- set_invalid_content_error (err, "Contents '%s' of integer element not a number", text);
- goto out;
+ set_invalid_content_error (err, "Content '%s' of integer element is not a number", text);
+ return;
}
- break;
-
- case TYPE_STRING:
+ }
+ else if (stype_is_string (instruction.type))
+ {
if (!decode_text (text, &instruction.u.string.value))
{
- set_invalid_content_error (err, "Contents '%s' of text element is illformed", text);
- goto out;
+ set_invalid_content_error (err, "Content '%s' of text element is ill-formed", text);
+ return;
}
- break;
-
- default:
- g_assert_not_reached();
- break;
+ }
+ else
+ {
+ g_assert_not_reached();
}
g_array_append_val (build->instructions, instruction);
-
- out:
- g_free (free_me);
}
static void
free_instructions (Instruction *instructions, int n_instructions)
{
int i;
-
+
for (i = 0; i < n_instructions; ++i)
{
Instruction *instruction = &(instructions[i]);
-
- if (instruction->name)
- g_free (instruction->name);
-
- if (instruction->kind == VALUE && instruction->type == TYPE_STRING)
+
+ if (stype_is_string (instruction->type))
g_free (instruction->u.string.value);
}
@@ -1012,11 +501,11 @@ process_instruction_pairs (Instruction *first)
{
Instruction *instruction;
int n_elements;
-
+
g_assert (first->kind == BEGIN);
-
+
instruction = first + 1;
-
+
n_elements = 0;
while (instruction->kind != END)
{
@@ -1030,25 +519,27 @@ process_instruction_pairs (Instruction *first)
{
instruction++;
}
-
+
n_elements++;
}
-
+
first->u.begin.n_elements = n_elements;
first->u.begin.end_instruction = instruction;
-
+
instruction->u.end.begin_instruction = first;
-
+
return instruction + 1;
}
-
+
static gboolean
-post_process_read_instructions (Instruction *instructions, int n_instructions, GError **err)
+post_process_read_instructions (Instruction *instructions,
+ int n_instructions,
+ GError **err)
{
gboolean retval = TRUE;
GHashTable *instructions_by_id;
int i;
-
+
/* count list instructions, check pointers */
process_instruction_pairs (instructions);
@@ -1057,34 +548,33 @@ post_process_read_instructions (Instruction *instructions, int n_instructions, G
for (i = 0; i < n_instructions; ++i)
{
Instruction *instruction = &(instructions[i]);
-
+
if (instruction->kind == BEGIN)
{
int id = instruction->u.begin.id;
-
+
if (id)
g_hash_table_insert (instructions_by_id, GINT_TO_POINTER (id), instruction);
}
}
-
+
/* Make pointer instructions point to the corresponding element */
for (i = 0; i < n_instructions; ++i)
{
Instruction *instruction = &(instructions[i]);
-
- if (instruction->kind == VALUE &&
- instruction->type == TYPE_POINTER)
+
+ if (stype_is_pointer (instruction->type))
{
int target_id = instruction->u.pointer.target_id;
-
+
if (target_id)
{
Instruction *target = g_hash_table_lookup (instructions_by_id,
GINT_TO_POINTER (target_id));
-
+
if (target)
{
- if (instruction->u.pointer.target_type == target->type)
+ if (stype_get_target_type (instruction->type) == target->type)
{
instruction->u.pointer.target_instruction = target;
}
@@ -1112,12 +602,16 @@ post_process_read_instructions (Instruction *instructions, int n_instructions, G
}
g_hash_table_destroy (instructions_by_id);
-
+
return retval;
}
static Instruction *
-build_instructions (const char *contents, SFormat *format, int *n_instructions, GError **err)
+build_instructions (const char *contents,
+ gsize length,
+ SFormat *format,
+ int *n_instructions,
+ GError **err)
{
BuildContext build;
GMarkupParseContext *parse_context;
@@ -1130,33 +624,48 @@ build_instructions (const char *contents, SFormat *format, int *n_instructions,
NULL, /* error */
};
- build.state = sformat_get_start_state (format);
+ build.context = scontext_new (format);
build.instructions = g_array_new (TRUE, TRUE, sizeof (Instruction));
parse_context = g_markup_parse_context_new (&parser, 0, &build, NULL);
+
+ while (length)
+ {
+ int bytes = MIN (length, 65536);
+
+ if (!g_markup_parse_context_parse (parse_context, contents, bytes, err))
+ {
+ free_instructions ((Instruction *)build.instructions->data, build.instructions->len);
+ return NULL;
+ }
+
+ contents += bytes;
+ length -= bytes;
+ }
- if (!g_markup_parse_context_parse (parse_context, contents, -1, err))
+ if (!g_markup_parse_context_end_parse (parse_context, err))
{
free_instructions ((Instruction *)build.instructions->data, build.instructions->len);
- return NULL;
+ return NULL;
}
-
- if (!sformat_is_end_state (format, build.state))
+
+ if (!scontext_is_finished (build.context))
{
set_invalid_content_error (err, "Premature end of file\n");
-
+
free_instructions ((Instruction *)build.instructions->data, build.instructions->len);
return NULL;
}
-
+
if (!post_process_read_instructions ((Instruction *)build.instructions->data,
build.instructions->len, err))
{
free_instructions ((Instruction *)build.instructions->data, build.instructions->len);
return NULL;
}
-
+
*n_instructions = build.instructions->len;
+
return (Instruction *)g_array_free (build.instructions, FALSE);
}
@@ -1168,23 +677,30 @@ sfile_load (const char *filename,
gchar *contents;
gsize length;
SFileInput *input;
-
- if (!g_file_get_contents (filename, &contents, &length, err))
- return NULL;
+ GMappedFile *file;
+
+ file = g_mapped_file_new (filename, FALSE, err);
+ if (!file)
+ return NULL;
+
+ contents = g_mapped_file_get_contents (file);
+ length = g_mapped_file_get_length (file);
+
+ madvise (contents, length, MADV_SEQUENTIAL);
input = g_new (SFileInput, 1);
- input->instructions = build_instructions (contents, format, &input->n_instructions, err);
+ input->instructions = build_instructions (contents, length, format, &input->n_instructions, err);
if (!input->instructions)
{
g_free (input);
- g_free (contents);
+ g_mapped_file_free (file);
return NULL;
}
-
- g_free (contents);
+ g_mapped_file_free (file);
+
input->current_instruction = input->instructions;
input->instructions_by_location = g_hash_table_new (g_direct_hash, g_direct_equal);
@@ -1197,7 +713,7 @@ struct SFileOutput
SFormat *format;
GArray *instructions;
GHashTable *objects;
- const State *state;
+ SContext *context;
};
SFileOutput *
@@ -1207,9 +723,9 @@ sfile_output_new (SFormat *format)
output->format = format;
output->instructions = g_array_new (TRUE, TRUE, sizeof (Instruction));
- output->state = sformat_get_start_state (format);
+ output->context = scontext_new (format);
output->objects = g_hash_table_new (g_direct_hash, g_direct_equal);
-
+
return output;
}
@@ -1219,14 +735,13 @@ sfile_begin_add_record (SFileOutput *file,
{
Instruction instruction;
- file->state = state_transition_begin (file->state, name, &instruction.type);
-
- g_return_if_fail (file->state);
- g_return_if_fail (is_record_type (instruction.type));
+ instruction.type = scontext_begin (file->context, name);
+
+ g_return_if_fail (instruction.type);
+ g_return_if_fail (stype_is_record (instruction.type));
instruction.kind = BEGIN;
- instruction.name = g_strdup (name);
-
+
g_array_append_val (file->instructions, instruction);
}
@@ -1235,15 +750,14 @@ sfile_begin_add_list (SFileOutput *file,
const char *name)
{
Instruction instruction;
-
- file->state = state_transition_begin (file->state, name, &instruction.type);
-
- g_return_if_fail (file->state);
- g_return_if_fail (is_list_type (instruction.type));
-
+
+ instruction.type = scontext_begin (file->context, name);
+
+ g_return_if_fail (instruction.type);
+ g_return_if_fail (stype_is_list (instruction.type));
+
instruction.kind = BEGIN;
- instruction.name = g_strdup (name);
-
+
g_array_append_val (file->instructions, instruction);
}
@@ -1253,98 +767,107 @@ sfile_end_add (SFileOutput *file,
gpointer object)
{
Instruction instruction;
-
+
if (object && g_hash_table_lookup (file->objects, object))
{
g_warning ("Adding the same object (%p) twice", object);
return;
}
- file->state = state_transition_end (file->state, name, &instruction.type);
-
- if (!file->state)
+ instruction.type = scontext_end (file->context, name);
+
+ if (!instruction.type)
{
g_warning ("invalid call of sfile_end_add()");
return;
}
-
+
instruction.kind = END;
- instruction.name = g_strdup (name);
instruction.u.end.object = object;
-
+
g_array_append_val (file->instructions, instruction);
-
+
if (object)
g_hash_table_insert (file->objects, object, object);
}
-static void
+static SType *
sfile_check_value (SFileOutput *file,
- const char *name,
- SType type)
+ const char *name)
{
- SType tmp_type;
+ SType *tmp_type;
- file->state = state_transition_begin (file->state, name, &tmp_type);
- g_return_if_fail (file->state && tmp_type == type);
-
- file->state = state_transition_text (file->state, &type, NULL);
- g_return_if_fail (file->state && tmp_type == type);
+ tmp_type = scontext_begin (file->context, name);
+ if (!tmp_type)
+ return NULL;
- file->state = state_transition_end (file->state, name, &type);
- g_return_if_fail (file->state && tmp_type == type);
+ tmp_type = scontext_text (file->context);
+ if (!tmp_type)
+ return NULL;
+
+ tmp_type = scontext_end (file->context, name);
+ if (!tmp_type)
+ return NULL;
+
+ return tmp_type;
}
void
-sfile_add_string (SFileOutput *file,
- const char *name,
- const char *string)
+sfile_add_string (SFileOutput *file,
+ const char *name,
+ const char *string)
{
Instruction instruction;
-
+
g_return_if_fail (g_utf8_validate (string, -1, NULL));
- sfile_check_value (file, name, TYPE_STRING);
-
+ instruction.type = sfile_check_value (file, name);
+ if (!instruction.type || !stype_is_string (instruction.type))
+ {
+ g_warning ("Invalid call to sfile_add_string()");
+ return;
+ }
instruction.kind = VALUE;
- instruction.type = TYPE_STRING;
- instruction.name = g_strdup (name);
instruction.u.string.value = g_strdup (string);
-
+
g_array_append_val (file->instructions, instruction);
}
void
-sfile_add_integer (SFileOutput *file,
- const char *name,
- int integer)
+sfile_add_integer (SFileOutput *file,
+ const char *name,
+ int integer)
{
Instruction instruction;
- sfile_check_value (file, name, TYPE_INTEGER);
-
+ instruction.type = sfile_check_value (file, name);
+ if (!instruction.type || !stype_is_integer (instruction.type))
+ {
+ g_warning ("Invalid call to sfile_add_integer()");
+ return;
+ }
instruction.kind = VALUE;
- instruction.type = TYPE_INTEGER;
- instruction.name = g_strdup (name);
instruction.u.integer.value = integer;
-
+
g_array_append_val (file->instructions, instruction);
}
void
-sfile_add_pointer (SFileOutput *file,
- const char *name,
- gpointer pointer)
+sfile_add_pointer (SFileOutput *file,
+ const char *name,
+ gpointer pointer)
{
Instruction instruction;
- sfile_check_value (file, name, TYPE_POINTER);
-
+ instruction.type = sfile_check_value (file, name);
+ if (!instruction.type || !stype_is_pointer (instruction.type))
+ {
+ g_warning ("Invalid call to sfile_add_pointer()");
+ return;
+ }
instruction.kind = VALUE;
- instruction.type = TYPE_POINTER;
- instruction.name = g_strdup (name);
instruction.u.pointer.target_object = pointer;
-
+
g_array_append_val (file->instructions, instruction);
}
@@ -1358,15 +881,15 @@ post_process_write_instructions (SFileOutput *sfile)
GHashTable *instructions_by_object;
process_instruction_pairs (instructions);
-
+
/* Set all id's to -1 and create map from objects to instructions */
-
+
instructions_by_object = g_hash_table_new (g_direct_hash, g_direct_equal);
for (i = 0; i < n_instructions; ++i)
{
Instruction *instruction = &(instructions[i]);
-
+
if (instruction->kind == BEGIN)
{
instruction->u.begin.id = -1;
@@ -1377,34 +900,34 @@ post_process_write_instructions (SFileOutput *sfile)
instruction->u.end.object, instruction);
}
}
-
+
/* Assign an id to all pointed-to instructions */
id = 1;
for (i = 0; i < n_instructions; ++i)
{
Instruction *instruction = &(instructions[i]);
-
- if (instruction->type == TYPE_POINTER)
+
+ if (stype_is_pointer (instruction->type))
{
if (instruction->u.pointer.target_object)
{
Instruction *target;
-
+
target =
g_hash_table_lookup (instructions_by_object,
instruction->u.pointer.target_object);
-
+
if (!target)
{
g_warning ("pointer has unknown target\n");
- return;
+ goto out;
}
-
+
g_assert (target->kind == END);
-
+
if (target->u.end.begin_instruction->u.begin.id == -1)
target->u.end.begin_instruction->u.begin.id = id++;
-
+
instruction->u.pointer.target_id =
target->u.end.begin_instruction->u.begin.id;
}
@@ -1414,26 +937,31 @@ post_process_write_instructions (SFileOutput *sfile)
}
}
}
-
+
+out:
+ g_hash_table_destroy (instructions_by_object);
}
static void
-add_indent (GString *output, int indent)
+add_indent (GString *output,
+ int indent)
{
int i;
-
+
for (i = 0; i < indent; ++i)
g_string_append_c (output, ' ');
}
static void
-add_integer (GString *output, int value)
+add_integer (GString *output,
+ int value)
{
g_string_append_printf (output, "%d", value);
}
static void
-add_string (GString *output, const char *str)
+add_string (GString *output,
+ const char *str)
{
char *escaped = g_markup_escape_text (str, -1);
g_string_append_c (output, '\"');
@@ -1443,10 +971,13 @@ add_string (GString *output, const char *str)
}
static void
-add_begin_tag (GString *output, int indent, const char *name, int id)
+add_begin_tag (GString *output,
+ int indent,
+ const char *name,
+ int id)
{
add_indent (output, indent);
-
+
if (id != -1)
g_string_append_printf (output, "<%s id=\"%d\">", name, id);
else
@@ -1454,7 +985,9 @@ add_begin_tag (GString *output, int indent, const char *name, int id)
}
static void
-add_end_tag (GString *output, int indent, const char *name)
+add_end_tag (GString *output,
+ int indent,
+ const char *name)
{
add_indent (output, indent);
g_string_append_printf (output, "</%s>", name);
@@ -1466,12 +999,6 @@ add_nl (GString *output)
g_string_append_c (output, '\n');
}
-static gboolean
-file_replace (const gchar *filename,
- const gchar *contents,
- gssize length,
- GError **error);
-
#if 0
static void
disaster (int status)
@@ -1490,7 +1017,7 @@ disaster (int status)
case BZ_OUTBUFF_FULL:
error = "BZ_OUTBUFF_FULL";
break;
-
+
default:
error = "Unknown error";
break;
@@ -1531,12 +1058,18 @@ bz2_compress (const guchar *input, int input_length,
*output = compressed_data;
else
g_free (compressed_data);
-
+
if (output_length)
*output_length = compressed_size;
}
#endif
+static const char *
+get_name (Instruction *inst)
+{
+ return stype_get_name (inst->type);
+}
+
gboolean
sfile_output_save (SFileOutput *sfile,
const char *filename,
@@ -1551,11 +1084,11 @@ sfile_output_save (SFileOutput *sfile,
guchar *compressed;
size_t compressed_size;
#endif
-
+
g_return_val_if_fail (sfile != NULL, FALSE);
-
+
instructions = (Instruction *)sfile->instructions->data;
-
+
post_process_write_instructions (sfile);
indent = 0;
@@ -1563,44 +1096,45 @@ sfile_output_save (SFileOutput *sfile,
for (i = 0; i < sfile->instructions->len; ++i)
{
Instruction *instruction = &(instructions[i]);
-
+
switch (instruction->kind)
{
case BEGIN:
- add_begin_tag (output, indent, instruction->name,
+ add_begin_tag (output, indent, get_name (instruction),
instruction->u.begin.id);
add_nl (output);
indent += 4;
break;
-
+
case END:
indent -= 4;
- add_end_tag (output, indent, instruction->name);
+ add_end_tag (output, indent, get_name (instruction));
add_nl (output);
break;
-
+
case VALUE:
- add_begin_tag (output, indent, instruction->name, -1);
- switch (instruction->type)
+ add_begin_tag (output, indent, get_name (instruction), -1);
+
+ if (stype_is_integer (instruction->type))
{
- case TYPE_INTEGER:
add_integer (output, instruction->u.integer.value);
- break;
-
- case TYPE_POINTER:
+ }
+ else if (stype_is_pointer (instruction->type))
+ {
add_integer (output, instruction->u.pointer.target_id);
- break;
-
- case TYPE_STRING:
+ }
+ else if (stype_is_string (instruction->type))
+ {
add_string (output, instruction->u.string.value);
- break;
}
- add_end_tag (output, 0, instruction->name);
- add_nl (output);
+
+ add_end_tag (output, 0, get_name (instruction));
+
+ add_nl (output);
break;
}
}
-
+
#if 0
/* FIXME - not10: bz2 compressing the output is probably
* interesting at some point. For now just make sure
@@ -1608,25 +1142,25 @@ sfile_output_save (SFileOutput *sfile,
*/
bz2_compress (output->str, output->len,
&compressed, &compressed_size);
-
+
g_free (compressed);
#endif
- retval = file_replace (filename, output->str, - 1, err);
+ retval = g_file_set_contents (filename, output->str, output->len, err);
g_string_free (output, TRUE);
-
+
return retval;
}
void
-sfile_input_free (SFileInput *file)
+sfile_input_free (SFileInput *file)
{
free_instructions (file->instructions, file->n_instructions);
-
+
g_hash_table_destroy (file->instructions_by_location);
-
+
g_free (file);
}
@@ -1635,346 +1169,12 @@ sfile_output_free (SFileOutput *sfile)
{
Instruction *instructions;
int n_instructions;
-
+
n_instructions = sfile->instructions->len;
instructions = (Instruction *)g_array_free (sfile->instructions, FALSE);
-
+
free_instructions (instructions, n_instructions);
-
+
g_hash_table_destroy (sfile->objects);
g_free (sfile);
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-/* A copy of g_file_replace() because I don't want to depend on
- * GLib HEAD
- */
-#include <errno.h>
-#include <sys/wait.h>
-#include <glib/gstdio.h>
-#include <unistd.h>
-
-static gboolean
-rename_file (const char *old_name,
- const char *new_name,
- GError **err)
-{
- errno = 0;
- if (g_rename (old_name, new_name) == -1)
- {
- int save_errno = errno;
- gchar *display_old_name = g_filename_display_name (old_name);
- gchar *display_new_name = g_filename_display_name (new_name);
-
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- "Failed to rename file '%s' to '%s': g_rename() failed: %s",
- display_old_name,
- display_new_name,
- g_strerror (save_errno));
-
- g_free (display_old_name);
- g_free (display_new_name);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-set_umask_permissions (int fd,
- GError **err)
-{
-#ifdef G_OS_WIN32
-
- return TRUE;
-
-#else
-
- /* All of this function is just to work around the fact that
- * there is no way to get the umask without changing it.
- *
- * We can't just change-and-reset the umask because that would
- * lead to a race condition if another thread tried to change
- * the umask in between the getting and the setting of the umask.
- * So we have to do the whole thing in a child process.
- */
-
- pid_t pid = fork ();
-
- if (pid == -1)
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- "Could not change file mode: fork() failed: %s",
- g_strerror (errno));
-
- return FALSE;
- }
- else if (pid == 0)
- {
- /* child */
- mode_t mask = umask (0666);
-
- errno = 0;
- if (fchmod (fd, 0666 & ~mask) == -1)
- _exit (errno);
- else
- _exit (0);
-
- return TRUE; /* To quiet gcc */
- }
- else
- {
- /* parent */
- int status;
-
- waitpid (pid, &status, 0);
-
- if (WIFEXITED (status))
- {
- int chmod_errno = WEXITSTATUS (status);
-
- if (chmod_errno == 0)
- {
- return TRUE;
- }
- else
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (chmod_errno),
- "Could not change file mode: chmod() failed: %s",
- g_strerror (chmod_errno));
-
- return FALSE;
- }
- }
- else if (WIFSIGNALED (status))
- {
- g_set_error (err,
- G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- "Could not change file mode: Child terminated by signal: %s",
- g_strsignal (WTERMSIG (status)));
-
- return FALSE;
- }
- else
- {
- /* This shouldn't happen */
- g_set_error (err,
- G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- "Could not change file mode: Child terminated abnormally");
- return FALSE;
- }
- }
-#endif
-}
-
-static gchar *
-write_to_temp_file (const gchar *contents,
- gssize length,
- const gchar *template,
- GError **err)
-{
- gchar *tmp_name;
- gchar *display_name;
- gchar *retval;
- FILE *file;
- gint fd;
- int save_errno;
-
- retval = NULL;
-
- tmp_name = g_strdup_printf ("%s.XXXXXX", template);
-
- errno = 0;
- fd = g_mkstemp (tmp_name);
- save_errno = errno;
- display_name = g_filename_display_name (tmp_name);
-
- if (fd == -1)
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- "Failed to create file '%s': %s",
- display_name, g_strerror (save_errno));
-
- goto out;
- }
-
- if (!set_umask_permissions (fd, err))
- {
- close (fd);
- g_unlink (tmp_name);
-
- goto out;
- }
-
- errno = 0;
- file = fdopen (fd, "wb");
- if (!file)
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- "Failed to open file '%s' for writing: fdopen() failed: %s",
- display_name,
- g_strerror (errno));
-
- close (fd);
- g_unlink (tmp_name);
-
- goto out;
- }
-
- if (length > 0)
- {
- size_t n_written;
-
- errno = 0;
-
- n_written = fwrite (contents, 1, length, file);
-
- if (n_written < length)
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- "Failed to write file '%s': fwrite() failed: %s",
- display_name,
- g_strerror (errno));
-
- fclose (file);
- g_unlink (tmp_name);
-
- goto out;
- }
- }
-
- errno = 0;
- if (fclose (file) == EOF)
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- "Failed to close file '%s': fclose() failed: %s",
- display_name,
- g_strerror (errno));
-
- g_unlink (tmp_name);
-
- goto out;
- }
-
- retval = g_strdup (tmp_name);
-
- out:
- g_free (tmp_name);
- g_free (display_name);
-
- return retval;
-}
-
-static gboolean
-file_replace (const gchar *filename,
- const gchar *contents,
- gssize length,
- GError **error)
-{
- gchar *tmp_filename;
- gboolean retval;
- GError *rename_error = NULL;
-
- g_return_val_if_fail (filename != NULL, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (contents != NULL || length == 0, FALSE);
- g_return_val_if_fail (length >= -1, FALSE);
-
- if (length == -1)
- length = strlen (contents);
-
- tmp_filename = write_to_temp_file (contents, length, filename, error);
-
- if (!tmp_filename)
- {
- retval = FALSE;
- goto out;
- }
-
- if (!rename_file (tmp_filename, filename, &rename_error))
- {
-#ifndef G_OS_WIN32
-
- g_unlink (tmp_filename);
- g_propagate_error (error, rename_error);
- retval = FALSE;
- goto out;
-
-#else /* G_OS_WIN32 */
-
- /* Renaming failed, but on Windows this may just mean
- * the file already exists. So if the target file
- * exists, try deleting it and do the rename again.
- */
- if (!g_file_test (filename, G_FILE_TEST_EXISTS))
- {
- g_unlink (tmp_filename);
- g_propagate_error (error, rename_error);
- retval = FALSE;
- goto out;
- }
-
- g_error_free (rename_error);
-
- if (g_unlink (filename) == -1)
- {
- gchar *display_filename = g_filename_display_name (filename);
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- "Existing file '%s' could not be removed: g_unlink() failed: %s",
- display_filename,
- g_strerror (errno));
-
- g_free (display_filename);
- g_unlink (tmp_filename);
- retval = FALSE;
- goto out;
- }
-
- if (!rename_file (tmp_filename, filename, error))
- {
- g_unlink (tmp_filename);
- retval = FALSE;
- goto out;
- }
-
-#endif
- }
-
- retval = TRUE;
-
- out:
- g_free (tmp_filename);
- return retval;
-}
-
diff --git a/sfile.h b/sfile.h
index 92a1810..20f8aa5 100644
--- a/sfile.h
+++ b/sfile.h
@@ -17,12 +17,30 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-typedef struct SFormat SFormat;
+#include "sformat.h"
+
typedef struct SFileInput SFileInput;
typedef struct SFileOutput SFileOutput;
-typedef guint SType;
+#if 0
+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
*
* Serializer *serializer_new (SerializerFormat *format);
@@ -42,6 +60,11 @@ typedef guint SType;
* For formats consider:
*
* Format *format_new (void);
+ * void format_free (void);
+ * Content *format_create_record (Format *format, Content *c1, ...);
+ * Content *format_create_list (Format *format, Content *t);
+ * Content *format_create_pointer (Format *format, Content *pointer_type);
+ *
* void format_set_record (Format *f, Content *r);
* Content *new_record (Content *c1, ...);
*
@@ -55,27 +78,30 @@ typedef guint SType;
* enums, optionals, selections, empties
*
*
+ * Other things:
+ *
+ * "selections" - when several different types are possible - would need lambda transitions in and out
+ *
+ * ability to allow 'ignored' elements that are simply skipped at parse time. This could become important
+ * for future-proofing files.
+ *
+ * unions maybe?
+ *
+ *
+ *
+ *
*==============================================
* Also think about versioning - apps will want to be able to read and write
* different versions of the format, and they want to be able to sniff the
* 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 - */
-SFormat *sformat_new (gpointer f);
-gpointer sformat_new_record (const char *name,
- SType *type,
- gpointer content,
- ...);
-gpointer sformat_new_list (const char *name,
- SType *type,
- gpointer content);
-gpointer sformat_new_pointer (const char *name,
- SType *target_type);
-gpointer sformat_new_integer (const char *name);
-gpointer sformat_new_string (const char *name);
-void sformat_free (SFormat *format);
/* - Reading - */
@@ -140,4 +166,4 @@ gboolean sfile_output_save (SFileOutput *sfile,
const char *filename,
GError **err);
-void sfile_output_free (SFileOutput *sfile);
+void sfile_output_free (SFileOutput *sfile);
diff --git a/sformat.c b/sformat.c
new file mode 100644
index 0000000..08a18bd
--- /dev/null
+++ b/sformat.c
@@ -0,0 +1,646 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004, Red Hat, Inc.
+ * Copyright 2004, 2005, 2006, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "sformat.h"
+
+typedef struct State State;
+typedef struct Transition Transition;
+typedef struct Fragment Fragment;
+
+/*
+ * Format
+ */
+struct SFormat
+{
+ State * begin;
+ State * end;
+
+ GQueue * types;
+ GQueue * transitions;
+ GQueue * states;
+};
+
+/*
+ * Type
+ */
+typedef enum
+{
+ TYPE_POINTER,
+ TYPE_STRING,
+ TYPE_INTEGER,
+ TYPE_RECORD,
+ TYPE_LIST,
+ TYPE_FORWARD
+} TypeKind;
+
+struct SType
+{
+ TypeKind kind;
+ char *name;
+ Transition *enter, *exit;
+ SType *target; /* If kind is TYPE_POINTER */
+};
+
+static void type_free (SType *type);
+
+/*
+ * Transition
+ */
+typedef enum
+{
+ BEGIN,
+ VALUE,
+ END
+} TransitionKind;
+
+struct Transition
+{
+ SType * type;
+ TransitionKind kind;
+ State * to;
+};
+
+static Transition *transition_new (SFormat *format,
+ TransitionKind kind,
+ SType *type,
+ State *from,
+ State *to);
+static void transition_free (Transition *transition);
+static void transition_set_to_state (Transition *transition,
+ State *to_state);
+
+/*
+ * State
+ */
+
+struct State
+{
+ GQueue *transitions;
+};
+
+static State *state_new (SFormat *format);
+static void state_add_transition (State *state,
+ Transition *transition);
+static void state_free (State *state);
+
+/*
+ * Format
+ */
+SFormat *
+sformat_new (void)
+{
+ /* FIXME: should probably be refcounted, and an SContext
+ * should have a ref on the format
+ */
+ SFormat *format = g_new0 (SFormat, 1);
+
+ format->begin = NULL;
+ format->end = NULL;
+
+ format->types = g_queue_new ();
+ format->states = g_queue_new ();
+ format->transitions = g_queue_new ();
+
+ return format;
+}
+
+void
+sformat_free (SFormat *format)
+{
+ GList *list;
+
+ for (list = format->types->head; list; list = list->next)
+ {
+ SType *type = list->data;
+
+ type_free (type);
+ }
+ g_queue_free (format->types);
+
+ for (list = format->states->head; list; list = list->next)
+ {
+ State *state = list->data;
+
+ state_free (state);
+ }
+ g_queue_free (format->states);
+
+ for (list = format->transitions->head; list; list = list->next)
+ {
+ Transition *transition = list->data;
+
+ transition_free (transition);
+ }
+ g_queue_free (format->transitions);
+
+ g_free (format);
+}
+
+void
+sformat_set_type (SFormat *format,
+ SType *type)
+{
+ format->begin = state_new (format);
+ format->end = state_new (format);
+
+ state_add_transition (format->begin, type->enter);
+ transition_set_to_state (type->exit, format->end);
+}
+
+/*
+ * Type
+ */
+static SType *
+type_new (SFormat *format,
+ TypeKind kind,
+ const char *name)
+{
+ SType *type = g_new0 (SType, 1);
+
+ type->kind = kind;
+ type->name = name? g_strdup (name) : NULL;
+ type->enter = NULL;
+ type->exit = NULL;
+ type->target = NULL;
+
+ g_queue_push_tail (format->types, type);
+
+ return type;
+}
+
+static SType *
+type_new_from_forward (SFormat *format,
+ TypeKind kind,
+ const char *name,
+ SForward *forward)
+{
+ SType *type;
+
+ if (forward)
+ {
+ type = (SType *)forward;
+ type->kind = kind;
+ type->name = g_strdup (name);
+ }
+ else
+ {
+ type = type_new (format, kind, name);
+ }
+
+ return type;
+}
+
+
+static SType *
+type_new_value (SFormat *format, TypeKind kind, const char *name)
+{
+ SType *type = type_new (format, kind, name);
+ State *before, *after;
+ Transition *value;
+
+ before = state_new (format);
+ after = state_new (format);
+
+ type->enter = transition_new (format, BEGIN, type, NULL, before);
+ type->exit = transition_new (format, END, type, after, NULL);
+ value = transition_new (format, VALUE, type, before, after);
+
+ return type;
+}
+
+static void
+type_free (SType *type)
+{
+ g_free (type->name);
+ g_free (type);
+}
+
+SForward *
+sformat_declare_forward (SFormat *format)
+{
+ SType *type = type_new (format, TYPE_FORWARD, NULL);
+
+ return (SForward *)type;
+}
+
+
+static GQueue *
+expand_varargs (SType *content1,
+ va_list args)
+{
+ GQueue *types = g_queue_new ();
+ SType *type;
+
+ g_queue_push_tail (types, content1);
+
+ type = va_arg (args, SType *);
+ while (type)
+ {
+ g_queue_push_tail (types, type);
+ type = va_arg (args, SType *);
+ }
+
+ return types;
+}
+
+SType *
+sformat_make_record (SFormat *format,
+ const char *name,
+ SForward *forward,
+ SType *content,
+ ...)
+{
+ SType *type;
+ va_list args;
+ GQueue *types;
+ GList *list;
+ State *begin, *state;
+
+ /* Build queue of child types */
+ va_start (args, content);
+ types = expand_varargs (content, args);
+ va_end (args);
+
+ /* chain types together */
+ state = begin = state_new (format);
+
+ for (list = types->head; list != NULL; list = list->next)
+ {
+ SType *child_type = list->data;
+
+ state_add_transition (state, child_type->enter);
+
+ state = state_new (format);
+
+ transition_set_to_state (child_type->exit, state);
+ }
+
+ g_queue_free (types);
+
+ /* create and return the new type */
+ type = type_new_from_forward (format, TYPE_RECORD, name, forward);
+ type->enter = transition_new (format, BEGIN, type, NULL, begin);
+ type->exit = transition_new (format, END, type, state, NULL);
+
+ return type;
+}
+
+SType *
+sformat_make_list (SFormat *format,
+ const char *name,
+ SForward *forward,
+ SType *child_type)
+{
+ SType *type;
+ State *list_state;
+
+ type = type_new_from_forward (format, TYPE_LIST, name, forward);
+
+ list_state = state_new (format);
+
+ type->enter = transition_new (format, BEGIN, type, NULL, list_state);
+ type->exit = transition_new (format, END, type, list_state, NULL);
+
+ state_add_transition (list_state, child_type->enter);
+ transition_set_to_state (child_type->exit, list_state);
+
+ return type;
+}
+
+SType *
+sformat_make_pointer (SFormat *format,
+ const char *name,
+ SForward *forward)
+{
+ SType *type = type_new_value (format, TYPE_POINTER, name);
+ type->target = (SType *)forward;
+
+ return type;
+}
+
+SType *
+sformat_make_integer (SFormat *format,
+ const char *name)
+{
+ return type_new_value (format, TYPE_INTEGER, name);
+}
+
+SType *
+sformat_make_string (SFormat *format,
+ const char *name)
+{
+ return type_new_value (format, TYPE_STRING, name);
+}
+
+
+
+gboolean
+stype_is_record (SType *type)
+{
+ return type->kind == TYPE_RECORD;
+}
+
+gboolean
+stype_is_list (SType *type)
+{
+ return type->kind == TYPE_LIST;
+}
+
+gboolean
+stype_is_pointer (SType *type)
+{
+ return type->kind == TYPE_POINTER;
+}
+
+gboolean
+stype_is_integer (SType *type)
+{
+ return type->kind == TYPE_INTEGER;
+}
+
+gboolean
+stype_is_string (SType *type)
+{
+ return type->kind == TYPE_STRING;
+}
+
+SType *
+stype_get_target_type (SType *type)
+{
+ g_return_val_if_fail (stype_is_pointer (type), NULL);
+
+ return type->target;
+}
+
+const char *
+stype_get_name (SType *type)
+{
+ return type->name;
+}
+
+/* Consider adding unions at some point
+ *
+ * To be useful they should probably be anonymous, so that
+ * the union itself doesn't have a representation in the
+ * xml file.
+ *
+ * API:
+ * sformat_new_union (gpointer content1, ...);
+ *
+ * char *content = begin_get_union ();
+ * if (strcmp (content, ...) == 0)
+ * get_pointer ();
+ * else if (strcmp (content, ...) == 0)
+ *
+ * ;
+ *
+ * Annoying though, that we then won't have the nice one-to-one
+ * correspondence between begin()/end() calls and <element></element>s
+ * Actually, we will probably have to have <union>asdlfkj</union>
+ * elements. That will make things a lot easier, and unions are
+ * still pretty useful if you put big things like lists in them.
+ *
+ * Or maybe just give them a name ...
+ *
+ * We may also consider adding anonymous records. These will
+ * not be able to have pointers associated with them though
+ * (because there wouldn't be a natural place
+ *
+ *
+ * Also consider adding the following data types:
+ *
+ * - Binary blobs of data, stored as base64 perhaps
+ *
+ * - floating point values. How do we store those portably
+ * without losing precision? Gnumeric may know.
+ *
+ * - enums, stored as strings
+ *
+ * - booleans.
+ */
+
+
+/*
+ * State
+ */
+static State *
+state_new (SFormat *format)
+{
+ State *state = g_new0 (State, 1);
+ state->transitions = g_queue_new ();
+
+ g_queue_push_tail (format->states, state);
+
+ return state;
+}
+
+static void
+state_add_transition (State *state,
+ Transition *transition)
+{
+ g_queue_push_tail (state->transitions, transition);
+}
+
+static void
+state_free (State *state)
+{
+ g_queue_free (state->transitions);
+ g_free (state);
+}
+
+/*
+ * Transition
+ */
+static Transition *
+transition_new (SFormat *format,
+ TransitionKind kind,
+ SType *type,
+ State *from,
+ State *to)
+{
+ Transition *transition = g_new0 (Transition, 1);
+
+ transition->type = type;
+ transition->kind = kind;
+ transition->to = to;
+
+ if (from)
+ state_add_transition (from, transition);
+
+ g_queue_push_tail (format->transitions, transition);
+
+ return transition;
+}
+
+static void
+transition_free (Transition *transition)
+{
+ g_free (transition);
+}
+
+static void
+transition_set_to_state (Transition *transition,
+ State *to_state)
+{
+ transition->to = to_state;
+}
+
+/* Context */
+struct SContext
+{
+ SFormat *format;
+ State *state;
+};
+
+SContext *
+scontext_new (SFormat *format)
+{
+ SContext *context = g_new0 (SContext, 1);
+
+ context->format = format;
+ context->state = format->begin;
+
+ return context;
+}
+
+static SType *
+do_transition (SContext *context,
+ TransitionKind kind,
+ const char *element)
+{
+ GList *list;
+
+ for (list = context->state->transitions->head; list; list = list->next)
+ {
+ Transition *transition = list->data;
+
+ if (transition->kind == kind)
+ {
+ if (kind == VALUE || strcmp (transition->type->name, element) == 0)
+ {
+ context->state = transition->to;
+ return transition->type;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+SType *
+scontext_begin (SContext *context,
+ const char *element)
+{
+ return do_transition (context, BEGIN, element);
+}
+
+SType *
+scontext_text (SContext *context)
+{
+ return do_transition (context, VALUE, NULL);
+}
+
+SType *
+scontext_end (SContext *context,
+ const char *element)
+{
+ return do_transition (context, END, element);
+}
+
+gboolean
+scontext_is_finished (SContext *context)
+{
+ return context->state == context->format->end;
+}
+
+void
+scontext_free (SContext *context)
+{
+ g_free (context);
+}
+
+
+
+/* assorted stuff */
+#if 0
+static const State *
+state_transition_check (const State *state,
+ const char *element,
+ TransitionKind kind,
+ SType *type)
+{
+ GList *list;
+
+ for (list = state->transitions->head; list; list = list->next)
+ {
+ Transition *transition = list->data;
+
+ if (transition->kind == kind &&
+ strcmp (element, transition->element) == 0)
+ {
+ *type = transition->type;
+ return transition->to;
+ }
+ }
+
+ return NULL;
+}
+
+static const State *
+state_transition_begin (const State *state, const char *element, SType *type)
+{
+ return state_transition_check (state, element, BEGIN, type);
+}
+
+static const State *
+state_transition_end (const State *state, const char *element, SType *type)
+{
+ return state_transition_check (state, element, END, type);
+}
+
+static const State *
+state_transition_text (const State *state, SType *type, SType *target_type)
+{
+ GList *list;
+
+ for (list = state->transitions->head; list; list = list->next)
+ {
+ Transition *transition = list->data;
+
+ if (transition->kind == VALUE)
+ {
+ *type = transition->type;
+
+ if (*type == TYPE_POINTER && target_type)
+ *target_type = transition->target_type;
+
+ /* There will never be more than one allowed value transition for
+ * a given state
+ */
+ return transition->to;
+ }
+ }
+
+ return NULL;
+}
+
+#endif
diff --git a/sformat.h b/sformat.h
new file mode 100644
index 0000000..484f7a6
--- /dev/null
+++ b/sformat.h
@@ -0,0 +1,67 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004, Red Hat, Inc.
+ * Copyright 2004, 2005, 2006, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SFORMAT_H
+#define SFORMAT_H
+
+typedef struct SFormat SFormat;
+typedef struct SType SType;
+typedef struct SForward SForward;
+typedef struct SContext SContext;
+
+SFormat *sformat_new (void);
+void sformat_free (SFormat *format);
+void sformat_set_type (SFormat *format,
+ SType *type);
+SForward *sformat_declare_forward (SFormat *format);
+SType *sformat_make_record (SFormat *format,
+ const char *name,
+ SForward *forward,
+ SType *content,
+ ...);
+SType *sformat_make_list (SFormat *format,
+ const char *name,
+ SForward *forward,
+ SType *type);
+SType *sformat_make_pointer (SFormat *format,
+ const char *name,
+ SForward *type);
+SType *sformat_make_integer (SFormat *format,
+ const char *name);
+SType *sformat_make_string (SFormat *format,
+ const char *name);
+
+gboolean stype_is_record (SType *type);
+gboolean stype_is_list (SType *type);
+gboolean stype_is_pointer (SType *type);
+gboolean stype_is_integer (SType *type);
+gboolean stype_is_string (SType *type);
+SType *stype_get_target_type (SType *type);
+const char *stype_get_name (SType *type);
+
+SContext *scontext_new (SFormat *format);
+SType *scontext_begin (SContext *context,
+ const char *element);
+SType *scontext_text (SContext *context);
+SType *scontext_end (SContext *context,
+ const char *element);
+gboolean scontext_is_finished (SContext *context);
+void scontext_free (SContext *context);
+
+#endif
diff --git a/signal-handler.c b/signal-handler.c
new file mode 100644
index 0000000..a241d25
--- /dev/null
+++ b/signal-handler.c
@@ -0,0 +1,215 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
+
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include "watch.h"
+#include "signal-handler.h"
+
+typedef struct SignalWatch SignalWatch;
+struct SignalWatch
+{
+ int signo;
+ SignalFunc handler;
+ gpointer user_data;
+
+ struct sigaction old_action;
+
+ SignalWatch * next;
+};
+
+static int read_end = -1;
+static int write_end = -1;
+static SignalWatch *signal_watches = NULL;
+
+static SignalWatch *
+lookup_signal_watch (int signo)
+{
+ SignalWatch *w;
+
+ for (w = signal_watches; w != NULL; w = w->next)
+ {
+ if (w->signo == signo)
+ return w;
+ }
+
+ return NULL;
+}
+
+static void
+remove_signal_watch (SignalWatch *watch)
+{
+ SignalWatch *prev, *w;
+
+ g_return_if_fail (watch != NULL);
+
+ prev = NULL;
+ for (w = signal_watches; w != NULL; w = w->next)
+ {
+ if (w == watch)
+ {
+ if (prev)
+ prev->next = w->next;
+ else
+ signal_watches = w->next;
+
+ break;
+ }
+
+ prev = w;
+ }
+}
+
+static void
+signal_handler (int signo,
+ siginfo_t *info,
+ void *data)
+{
+ /* FIXME: I suppose we should handle short
+ * and non-successful writes ...
+ *
+ * And also, there is a deadlock if so many signals arrive that
+ * write() blocks. Then we will be stuck right here, and the
+ * main loop will never run. Kinda hard to fix without dropping
+ * signals ...
+ *
+ */
+ write (write_end, &signo, sizeof (int));
+}
+
+static void
+on_read (gpointer data)
+{
+ SignalWatch *watch;
+ int signo;
+
+ /* FIXME: handle short read I suppose */
+ read (read_end, &signo, sizeof (int));
+
+ watch = lookup_signal_watch (signo);
+
+ if (watch)
+ watch->handler (signo, watch->user_data);
+}
+
+static gboolean
+create_pipe (int *read_end,
+ int *write_end,
+ GError **err)
+{
+ int p[2];
+
+ if (pipe (p) < 0)
+ {
+ /* FIXME - create an error */
+ return FALSE;
+ }
+
+ /* FIXME: We should probably make the fd's non-blocking */
+ if (read_end)
+ *read_end = p[0];
+
+ if (write_end)
+ *write_end = p[1];
+
+ return TRUE;
+}
+
+static gboolean
+install_signal_handler (int signo,
+ struct sigaction *old_action,
+ GError **err)
+{
+ struct sigaction action;
+
+ memset (&action, 0, sizeof (action));
+
+ action.sa_sigaction = signal_handler;
+ sigemptyset (&action.sa_mask);
+ action.sa_flags = SA_SIGINFO;
+
+ if (sigaction (signo, &action, old_action) < 0)
+ {
+ /* FIXME - create an error */
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+static void
+signal_watch_free (SignalWatch *watch)
+{
+ remove_signal_watch (watch);
+
+ g_free (watch);
+}
+
+gboolean
+signal_set_handler (int signo,
+ SignalFunc handler,
+ gpointer data,
+ GError **err)
+{
+ SignalWatch *watch;
+
+ g_return_val_if_fail (lookup_signal_watch (signo) == NULL, FALSE);
+
+ if (read_end == -1)
+ {
+ if (!create_pipe (&read_end, &write_end, err))
+ return FALSE;
+
+ fd_add_watch (read_end, NULL);
+ fd_set_read_callback (read_end, on_read);
+ }
+
+ watch = g_new0 (SignalWatch, 1);
+
+ watch->signo = signo;
+ watch->handler = handler;
+ watch->user_data = data;
+ watch->next = signal_watches;
+ signal_watches = watch;
+
+ if (!install_signal_handler (signo, &watch->old_action, err))
+ {
+ signal_watch_free (watch);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+signal_unset_handler (int signo)
+{
+ SignalWatch *watch;
+
+ watch = lookup_signal_watch (signo);
+
+ g_return_if_fail (watch != NULL);
+
+ sigaction (signo, &watch->old_action, NULL);
+
+ signal_watch_free (watch);
+}
diff --git a/signal-handler.h b/signal-handler.h
new file mode 100644
index 0000000..44f3dde
--- /dev/null
+++ b/signal-handler.h
@@ -0,0 +1,27 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
+
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+typedef void (* SignalFunc) (int signo, gpointer data);
+
+gboolean signal_set_handler (int signo,
+ SignalFunc handler,
+ gpointer data,
+ GError **err);
+void signal_unset_handler (int signo);
diff --git a/stackstash.c b/stackstash.c
index 9f66f15..44912e4 100644
--- a/stackstash.c
+++ b/stackstash.c
@@ -19,200 +19,369 @@
#include "stackstash.h"
-typedef struct StackNode StackNode;
-
-struct StackNode
+struct StackStash
{
- StackNode * parent;
- gpointer address;
- StackNode * siblings;
- StackNode * children;
- StackNode * next; /* next leaf with the same pid */
- int size;
+ int ref_count;
+ StackNode * root;
+ GHashTable * nodes_by_data;
+ GDestroyNotify destroy;
+
+ StackNode * cached_nodes;
+ GPtrArray * blocks;
};
-struct StackStash
+static void
+decorate_node (StackNode *node,
+ StackStash *stash)
{
- StackNode *root;
- GHashTable *leaves_by_process;
-};
+ StackNode *n;
+
+ if (!node)
+ return;
+
+ decorate_node (node->siblings, stash);
+ decorate_node (node->children, stash);
+
+ node->next = g_hash_table_lookup (stash->nodes_by_data, &node->data);
+ g_hash_table_insert (stash->nodes_by_data, &node->data, node);
+
+ /* FIXME: This could be done more efficiently
+ * by keeping track of the ancestors we have seen.
+ */
+ node->toplevel = TRUE;
+ for (n = node->parent; n != NULL; n = n->parent)
+ {
+ if (n->data == node->data)
+ {
+ node->toplevel = FALSE;
+ break;
+ }
+ }
+}
+
+static unsigned int
+address_hash (gconstpointer key)
+{
+ const uint64_t *addr = key;
+
+ return *addr;
+}
+
+static gboolean
+address_equal (gconstpointer key1, gconstpointer key2)
+{
+ const uint64_t *addr1 = key1;
+ const uint64_t *addr2 = key2;
+
+ return *addr1 == *addr2;
+}
+
+static void
+stack_stash_decorate (StackStash *stash)
+{
+ if (stash->nodes_by_data)
+ return;
+
+ stash->nodes_by_data = g_hash_table_new (address_hash, address_equal);
+
+ decorate_node (stash->root, stash);
+}
+
+static void
+free_key (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ GDestroyNotify destroy = data;
+ uint64_t u64 = *(uint64_t *)key;
+
+ destroy (U64_TO_POINTER (u64));
+}
-static StackNode *
-stack_node_new (void)
+static void
+stack_stash_undecorate (StackStash *stash)
+{
+ if (stash->nodes_by_data)
+ {
+ if (stash->destroy)
+ {
+ g_hash_table_foreach (
+ stash->nodes_by_data, free_key, stash->destroy);
+ }
+
+ g_hash_table_destroy (stash->nodes_by_data);
+ stash->nodes_by_data = NULL;
+ }
+}
+
+static GHashTable *
+get_nodes_by_data (StackStash *stash)
{
- StackNode *node = g_new (StackNode, 1);
+ if (!stash->nodes_by_data)
+ stack_stash_decorate (stash);
+
+ return stash->nodes_by_data;
+}
+
+StackNode *
+stack_node_new (StackStash *stash)
+{
+ StackNode *node;
+
+ if (!stash->cached_nodes)
+ {
+#define BLOCK_SIZE 32768
+#define N_NODES (BLOCK_SIZE / sizeof (StackNode))
+
+ StackNode *block = g_malloc (BLOCK_SIZE);
+ int i;
+
+ for (i = 0; i < N_NODES; ++i)
+ {
+ block[i].next = stash->cached_nodes;
+ stash->cached_nodes = &(block[i]);
+ }
+
+ g_ptr_array_add (stash->blocks, block);
+ }
+
+ node = stash->cached_nodes;
+ stash->cached_nodes = node->next;
+
node->siblings = NULL;
node->children = NULL;
- node->address = NULL;
+ node->data = 0;
node->parent = NULL;
- node->next = NULL;
node->size = 0;
+ node->next = NULL;
+ node->total = 0;
+
return node;
}
-static void
-stack_node_destroy (gpointer p)
+/* "destroy", if non-NULL, is called once on every address */
+static StackStash *
+create_stack_stash (GDestroyNotify destroy)
{
- StackNode *node = p;
- if (node)
- {
- stack_node_destroy (node->siblings);
- stack_node_destroy (node->children);
- g_free (node);
- }
+ StackStash *stash = g_new (StackStash, 1);
+
+ stash->root = NULL;
+ stash->nodes_by_data = NULL;
+ stash->ref_count = 1;
+ stash->destroy = destroy;
+
+ stash->cached_nodes = NULL;
+ stash->blocks = g_ptr_array_new ();
+
+ return stash;
}
/* Stach */
StackStash *
-stack_stash_new (void)
+stack_stash_new (GDestroyNotify destroy)
{
- StackStash *stash = g_new (StackStash, 1);
+ return create_stack_stash (destroy);
+}
- stash->leaves_by_process =
- g_hash_table_new (g_direct_hash, g_direct_equal);
- stash->root = NULL;
- return stash;
+
+static void
+stack_stash_free (StackStash *stash)
+{
+ int i;
+
+ stack_stash_undecorate (stash);
+
+ for (i = 0; i < stash->blocks->len; ++i)
+ g_free (stash->blocks->pdata[i]);
+
+ g_ptr_array_free (stash->blocks, TRUE);
+
+ g_free (stash);
}
-static StackNode *
-stack_node_add_trace (StackNode *node,
- GList *bottom,
- gint size,
- StackNode **leaf)
+StackNode *
+stack_stash_add_trace (StackStash *stash,
+ uint64_t *addrs,
+ int n_addrs,
+ int size)
{
- StackNode *match;
- StackNode *n;
+ StackNode **location = &(stash->root);
+ StackNode *parent = NULL;
+ int i;
- if (!bottom)
- {
- *leaf = NULL;
- return node;
- }
+ if (!n_addrs)
+ return NULL;
- if (!bottom->next)
+ if (stash->nodes_by_data)
+ stack_stash_undecorate (stash);
+
+ for (i = n_addrs - 1; i >= 0; --i)
{
- /* A leaf must always be separate, so pids can
- * point to them
+ StackNode *match = NULL;
+ StackNode *prev;
+
+ /* FIXME: On x86-64 we don't get proper stacktraces which means
+ * each node can have tons of children. That makes this loop
+ * here show up on profiles.
+ *
+ * Not sure what can be done about it aside from actually fixing
+ * x86-64 to get stacktraces.
*/
- match = NULL;
- }
- else
- {
- for (match = node; match != NULL; match = match->siblings)
+ prev = NULL;
+ for (match = *location; match; prev = match, match = match->siblings)
{
- if (match->address == bottom->data)
+ if (match->data == addrs[i])
+ {
+ if (prev)
+ {
+ /* move to front */
+ prev->siblings = match->siblings;
+ match->siblings = *location;
+ *location = match;
+ }
+
break;
+ }
}
- }
- if (!match)
- {
- match = stack_node_new ();
- match->address = bottom->data;
- match->siblings = node;
- node = match;
+ if (!match)
+ {
+ match = stack_node_new (stash);
+ match->data = addrs[i];
+ match->siblings = *location;
+ match->parent = parent;
+ *location = match;
+ }
+
+ match->total += size;
+
+ location = &(match->children);
+ parent = match;
}
- match->children =
- stack_node_add_trace (match->children, bottom->next, size, leaf);
+ parent->size += size;
- for (n = match->children; n; n = n->siblings)
- n->parent = match;
+ return parent;
+}
+
+static void
+do_callback (StackNode *node,
+ StackLink *trace,
+ StackFunction func,
+ gpointer data)
+{
+ StackLink link;
+
+ if (trace)
+ trace->prev = &link;
+
+ link.next = trace;
+ link.prev = NULL;
- if (!bottom->next)
+ while (node)
{
- match->size += size;
- *leaf = match;
+ link.data = node->data;
+
+ if (node->size)
+ func (&link, node->size, data);
+
+ do_callback (node->children, &link, func, data);
+
+ node = node->siblings;
}
- return node;
+ if (trace)
+ trace->prev = NULL;
}
void
-stack_stash_add_trace (StackStash *stash,
- Process *process,
- gulong *addrs,
- int n_addrs,
- int size)
+stack_stash_foreach (StackStash *stash,
+ StackFunction stack_func,
+ gpointer data)
{
- GList *trace;
- StackNode *leaf;
- int i;
+ do_callback (stash->root, NULL, stack_func, data);
+}
- if (!n_addrs)
- return;
-
- trace = NULL;
- for (i = 0; i < n_addrs; ++i)
- trace = g_list_prepend (trace, GINT_TO_POINTER (addrs[i]));
+void
+stack_node_foreach_trace (StackNode *node,
+ StackFunction func,
+ gpointer data)
+{
+ StackLink link;
- stash->root = stack_node_add_trace (stash->root, trace, size, &leaf);
+ link.next = NULL;
+ link.data = node->data;
+ link.prev = NULL;
- leaf->next = g_hash_table_lookup (
- stash->leaves_by_process, process);
- g_hash_table_insert (
- stash->leaves_by_process, process, leaf);
+ if (node->size)
+ func (&link, node->size, data);
+
+ do_callback (node->children, &link, func, data);
+}
- g_list_free (trace);
+void
+stack_stash_unref (StackStash *stash)
+{
+ stash->ref_count--;
+ if (stash->ref_count == 0)
+ stack_stash_free (stash);
}
-typedef struct CallbackInfo
+StackStash *
+stack_stash_ref (StackStash *stash)
{
- StackFunction func;
- gpointer data;
-} CallbackInfo;
+ stash->ref_count++;
+ return stash;
+}
-static void
-do_callback (gpointer key, gpointer value, gpointer data)
+StackNode *
+stack_stash_find_node (StackStash *stash,
+ gpointer data)
{
- CallbackInfo *info = data;
- Process *process = key;
- StackNode *n;
- StackNode *leaf = value;
- while (leaf)
- {
- GSList *trace;
+ uint64_t u64 = POINTER_TO_U64 (data);
+
+ g_return_val_if_fail (stash != NULL, NULL);
+
+ return g_hash_table_lookup (get_nodes_by_data (stash), &u64);
+}
- trace = NULL;
- for (n = leaf; n; n = n->parent)
- trace = g_slist_prepend (trace, n->address);
+typedef struct
+{
+ StackNodeFunc func;
+ gpointer data;
+} Info;
- info->func (process, trace, leaf->size, info->data);
+static void
+do_foreach (gpointer key, gpointer value, gpointer data)
+{
+ Info *info = data;
- g_slist_free (trace);
-
- leaf = leaf->next;
- }
+ info->func (value, info->data);
}
void
-stack_stash_foreach (StackStash *stash,
- StackFunction stack_func,
- gpointer data)
+stack_stash_foreach_by_address (StackStash *stash,
+ StackNodeFunc func,
+ gpointer data)
{
- CallbackInfo info;
- info.func = stack_func;
+ Info info;
+ info.func = func;
info.data = data;
- g_hash_table_foreach (stash->leaves_by_process, do_callback, &info);
+ g_hash_table_foreach (get_nodes_by_data (stash), do_foreach, &info);
}
-static void
-stack_node_free (StackNode *node)
+StackNode *
+stack_stash_get_root (StackStash *stash)
{
- if (!node)
- return;
-
- stack_node_free (node->siblings);
- stack_node_free (node->children);
-
- g_free (node);
+ return stash->root;
}
void
-stack_stash_free (StackStash *stash)
+stack_stash_set_root (StackStash *stash,
+ StackNode *root)
{
- stack_node_free (stash->root);
- g_hash_table_destroy (stash->leaves_by_process);
- g_free (stash);
+ g_return_if_fail (stash->root == NULL);
+
+ stash->root = root;
}
diff --git a/stackstash.h b/stackstash.h
index aa058e1..4fad001 100644
--- a/stackstash.h
+++ b/stackstash.h
@@ -21,25 +21,66 @@
#define STACK_STASH_H
#include <glib.h>
-#include "process.h"
+#include <stdint.h>
typedef struct StackStash StackStash;
+typedef struct StackNode StackNode;
+typedef struct StackLink StackLink;
-typedef void (* StackFunction) (Process *process,
- GSList *trace,
- gint size,
- gpointer data);
+#define U64_TO_POINTER(u) ((void *)(intptr_t)u)
+#define POINTER_TO_U64(p) ((uint64_t)(intptr_t)p)
+
+struct StackNode
+{
+ uint64_t data;
+
+ guint total : 32;
+ guint size : 31;
+ guint toplevel : 1;
+
+ StackNode * parent;
+ StackNode * siblings;
+ StackNode * children;
+
+ StackNode * next;
+};
+
+struct StackLink
+{
+ uint64_t data;
+ StackLink *next;
+ StackLink *prev;
+};
+
+typedef void (* StackFunction) (StackLink *trace,
+ gint size,
+ gpointer data);
+
+typedef void (* StackNodeFunc) (StackNode *node,
+ gpointer data);
/* Stach */
-StackStash *stack_stash_new (void);
-void stack_stash_add_trace (StackStash *stash,
- Process *process,
- gulong *addrs,
- gint n_addrs,
- int size);
-void stack_stash_foreach (StackStash *stash,
- StackFunction stack_func,
- gpointer data);
-void stack_stash_free (StackStash *stash);
+StackStash *stack_stash_new (GDestroyNotify destroy);
+StackNode * stack_node_new (StackStash *stash);
+StackNode * stack_stash_add_trace (StackStash *stash,
+ uint64_t *addrs,
+ gint n_addrs,
+ int size);
+void stack_stash_foreach (StackStash *stash,
+ StackFunction stack_func,
+ gpointer data);
+void stack_node_foreach_trace (StackNode *node,
+ StackFunction stack_func,
+ gpointer data);
+StackNode *stack_stash_find_node (StackStash *stash,
+ gpointer address);
+void stack_stash_foreach_by_address (StackStash *stash,
+ StackNodeFunc func,
+ gpointer data);
+StackNode *stack_stash_get_root (StackStash *stash);
+StackStash *stack_stash_ref (StackStash *stash);
+void stack_stash_unref (StackStash *stash);
+void stack_stash_set_root (StackStash *stash,
+ StackNode *root);
#endif
diff --git a/sysprof-cli.c b/sysprof-cli.c
new file mode 100644
index 0000000..90f5b13
--- /dev/null
+++ b/sysprof-cli.c
@@ -0,0 +1,151 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2005, Lorenzo Colitti
+ * Copyright 2005, Soren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <glib.h>
+#include <stdio.h>
+
+#include "stackstash.h"
+#include "profile.h"
+#include "watch.h"
+#include "signal-handler.h"
+#include "collector.h"
+
+typedef struct Application Application;
+struct Application
+{
+ Collector * collector;
+ char * outfile;
+ GMainLoop * main_loop;
+};
+
+static void
+dump_data (Application *app)
+{
+ GError *err = NULL;
+ Profile *profile;
+
+ printf ("Saving profile (%d samples) in %s ... ",
+ collector_get_n_samples (app->collector),
+ app->outfile);
+ fflush (stdout);
+
+ collector_stop (app->collector);
+
+ profile = collector_create_profile (app->collector);
+ profile_save (profile, app->outfile, &err);
+
+ if (err)
+ {
+ printf ("failed\n");
+ fprintf (stderr, "Error saving %s: %s\n", app->outfile, err->message);
+ exit (1);
+ }
+ else
+ {
+ printf ("done\n\n");
+ }
+}
+
+static void
+signal_handler (int signo,
+ gpointer data)
+{
+ Application *app = data;
+
+ dump_data (app);
+
+ while (g_main_context_iteration (NULL, FALSE))
+ ;
+
+ g_main_loop_quit (app->main_loop);
+}
+
+static char *
+usage_msg (const char *name)
+{
+ return g_strdup_printf (
+ "Usage: \n"
+ " %s <outfile>\n"
+ "\n"
+ "On SIGTERM or SIGINT (Ctrl-C) the profile will be written to <outfile>",
+ name);
+}
+
+static gboolean
+file_exists_and_is_dir (const char *name)
+{
+ return
+ g_file_test (name, G_FILE_TEST_EXISTS) &&
+ g_file_test (name, G_FILE_TEST_IS_DIR);
+}
+
+static void
+die (const char *err_msg)
+{
+ if (err_msg)
+ fprintf (stderr, "\n%s\n\n", err_msg);
+
+ exit (-1);
+}
+
+int
+main (int argc, char *argv[])
+{
+ Application *app = g_new0 (Application, 1);
+ GError *err;
+
+ app->collector = collector_new (FALSE, NULL, NULL);
+ app->outfile = g_strdup (argv[1]);
+ app->main_loop = g_main_loop_new (NULL, 0);
+
+ err = NULL;
+
+ if (!collector_start (app->collector, &err))
+ die (err->message);
+
+ if (argc < 2)
+ die (usage_msg (argv[0]));
+
+ if (!signal_set_handler (SIGTERM, signal_handler, app, &err))
+ die (err->message);
+
+ if (!signal_set_handler (SIGINT, signal_handler, app, &err))
+ die (err->message);
+
+ if (file_exists_and_is_dir (app->outfile))
+ {
+ char *msg = g_strdup_printf ("Can't write to %s: is a directory\n",
+ app->outfile);
+ die (msg);
+ }
+
+ g_main_loop_run (app->main_loop);
+
+ signal_unset_handler (SIGTERM);
+ signal_unset_handler (SIGINT);
+
+ return 0;
+}
diff --git a/sysprof-icon-16.png b/sysprof-icon-16.png
new file mode 100644
index 0000000..bc1cfe4
--- /dev/null
+++ b/sysprof-icon-16.png
Binary files differ
diff --git a/sysprof-icon-24.png b/sysprof-icon-24.png
new file mode 100644
index 0000000..55b2078
--- /dev/null
+++ b/sysprof-icon-24.png
Binary files differ
diff --git a/sysprof-icon-32.png b/sysprof-icon-32.png
new file mode 100644
index 0000000..6130df6
--- /dev/null
+++ b/sysprof-icon-32.png
Binary files differ
diff --git a/sysprof-icon-48.png b/sysprof-icon-48.png
new file mode 100644
index 0000000..bbb8210
--- /dev/null
+++ b/sysprof-icon-48.png
Binary files differ
diff --git a/sysprof-icon.png b/sysprof-icon.png
deleted file mode 100644
index 7325a21..0000000
--- a/sysprof-icon.png
+++ /dev/null
Binary files differ
diff --git a/sysprof.c b/sysprof.c
index 7024497..cc676a9 100644
--- a/sysprof.c
+++ b/sysprof.c
@@ -1,6 +1,6 @@
/* Sysprof -- Sampling, systemwide CPU profiler
* Copyright 2004, Red Hat, Inc.
- * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann
+ * Copyright 2004, 2005, 2006, 2007, 2008, Soeren Sandmann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,26 +16,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-
#include <config.h>
-#include <stdio.h>
#include <gtk/gtk.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
#include <glade/glade.h>
#include <errno.h>
#include <glib/gprintf.h>
-#include <sys/wait.h>
-#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
-#include "binfile.h"
-#include "watch.h"
-#include "module/sysprof-module.h"
-#include "stackstash.h"
-#include "profile.h"
+#include "footreestore.h"
#include "treeviewutils.h"
+#include "profile.h"
+#include "collector.h"
/* FIXME - not10 */
#define _(a) a
@@ -53,88 +46,99 @@ typedef enum
struct Application
{
- int input_fd;
+ Collector * collector;
+
State state;
- StackStash * stash;
-
- GtkWidget * main_window;
GdkPixbuf * icon;
-
+
+ GtkWidget * main_window;
+
GtkTreeView * object_view;
GtkTreeView * callers_view;
GtkTreeView * descendants_view;
-
+
GtkWidget * start_button;
GtkWidget * profile_button;
GtkWidget * reset_button;
GtkWidget * save_as_button;
GtkWidget * dummy_button;
-
+
GtkWidget * start_item;
GtkWidget * profile_item;
GtkWidget * reset_item;
GtkWidget * save_as_item;
GtkWidget * open_item;
-
+ GtkWidget * screenshot_item;
+ GtkWidget * about_item;
+ GtkWidget * quit_item;
+ GtkWidget * hpaned;
+ GtkWidget * vpaned;
+
+ GtkTreeSelection * object_selection;
+
GtkWidget * samples_label;
-
+ GtkWidget * samples_hbox;
+
+ gboolean screenshot_window_visible;
+ GtkWidget * screenshot_textview;
+ GtkWidget * screenshot_close_button;
+ GtkWidget * screenshot_window;
+
Profile * profile;
ProfileDescendant * descendants;
ProfileCaller * callers;
-
- int n_samples;
-
+
int timeout_id;
- int generating_profile;
+ int update_screenshot_id;
char * loaded_profile;
-
- gboolean profile_from_file; /* FIXME - not10: This is a kludge. Figure out how
- * to maintain the application model properly
- *
- * The fundamental issue is that the state of
- * widgets is controlled by two different
- * entities:
- *
- * The user clicks on them, changing their
- * state.
- *
- * The application model changes, changing their
- * state.
- *
- * Model/View/Controller is a possibility.
- */
- GTimeVal latest_reset;
+
+ gboolean inhibit_forced_redraw;
};
-static gboolean
-show_samples_timeout (gpointer data)
+static void update_screenshot_window (Application *app);
+
+static void
+show_samples (Application *app)
{
- Application *app = data;
char *label;
+ int n_samples;
switch (app->state)
{
case INITIAL:
- label = g_strdup ("Samples: 0");
+ n_samples = 0;
break;
-
+
case PROFILING:
+ n_samples = collector_get_n_samples (app->collector);
+ break;
+
case DISPLAYING:
- label = g_strdup_printf ("Samples: %d", app->n_samples);
+ n_samples = profile_get_size (app->profile);
break;
default:
g_assert_not_reached();
break;
}
-
+
+ label = g_strdup_printf ("%d", n_samples);
+
gtk_label_set_label (GTK_LABEL (app->samples_label), label);
-
+
g_free (label);
-
+}
+
+static gboolean
+show_samples_timeout (gpointer data)
+{
+ Application *app = data;
+
+ show_samples (app);
+
app->timeout_id = 0;
-
+
return FALSE;
}
@@ -152,11 +156,13 @@ update_sensitivity (Application *app)
gboolean sensitive_save_as_button;
gboolean sensitive_start_button;
gboolean sensitive_tree_views;
- gboolean sensitive_samples_label;
+ gboolean sensitive_samples_hbox;
gboolean sensitive_reset_button;
-
+
GtkWidget *active_radio_button;
-
+
+ gboolean has_samples;
+
switch (app->state)
{
case INITIAL:
@@ -165,27 +171,29 @@ update_sensitivity (Application *app)
sensitive_start_button = TRUE;
sensitive_reset_button = FALSE;
sensitive_tree_views = FALSE;
- sensitive_samples_label = FALSE;
+ sensitive_samples_hbox = FALSE;
active_radio_button = app->dummy_button;
break;
-
+
case PROFILING:
- sensitive_profile_button = (app->n_samples > 0);
- sensitive_save_as_button = (app->n_samples > 0);
- sensitive_reset_button = (app->n_samples > 0);
+ has_samples = (collector_get_n_samples (app->collector) > 0);
+
+ sensitive_profile_button = has_samples;
+ sensitive_save_as_button = has_samples;
+ sensitive_reset_button = has_samples;
sensitive_start_button = TRUE;
sensitive_tree_views = FALSE;
- sensitive_samples_label = TRUE;
+ sensitive_samples_hbox = TRUE;
active_radio_button = app->start_button;
break;
-
+
case DISPLAYING:
sensitive_profile_button = TRUE;
sensitive_save_as_button = TRUE;
sensitive_start_button = TRUE;
sensitive_tree_views = TRUE;
sensitive_reset_button = TRUE;
- sensitive_samples_label = FALSE;
+ sensitive_samples_hbox = FALSE;
active_radio_button = app->profile_button;
break;
@@ -193,15 +201,16 @@ update_sensitivity (Application *app)
g_assert_not_reached();
break;
}
-
- gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (active_radio_button), TRUE);
+
+ gtk_toggle_tool_button_set_active (
+ GTK_TOGGLE_TOOL_BUTTON (active_radio_button), TRUE);
/* "profile" widgets */
gtk_widget_set_sensitive (GTK_WIDGET (app->profile_button),
sensitive_profile_button);
gtk_widget_set_sensitive (GTK_WIDGET (app->profile_item),
sensitive_profile_button);
-
+
/* "save as" widgets */
gtk_widget_set_sensitive (GTK_WIDGET (app->save_as_button),
sensitive_save_as_button);
@@ -213,9 +222,9 @@ update_sensitivity (Application *app)
sensitive_start_button);
gtk_widget_set_sensitive (GTK_WIDGET (app->start_item),
sensitive_start_button);
-
+
#if 0
- /* FIXME - not10: gtk+ doesn't handle changes in sensitivity in response
+ /* FIXME - not10: gtk+ doesn't handle changes in sensitivity in response
* to a click on the same button very well
*/
gtk_widget_set_sensitive (GTK_WIDGET (app->reset_button),
@@ -223,177 +232,44 @@ update_sensitivity (Application *app)
gtk_widget_set_sensitive (GTK_WIDGET (app->reset_item),
sensitive_reset_button);
#endif
-
+
gtk_widget_set_sensitive (GTK_WIDGET (app->object_view), sensitive_tree_views);
gtk_widget_set_sensitive (GTK_WIDGET (app->callers_view), sensitive_tree_views);
gtk_widget_set_sensitive (GTK_WIDGET (app->descendants_view), sensitive_tree_views);
- gtk_widget_set_sensitive (GTK_WIDGET (app->samples_label), sensitive_samples_label);
+ gtk_widget_set_sensitive (GTK_WIDGET (app->samples_hbox), sensitive_samples_hbox);
- queue_show_samples (app);
+ if (app->screenshot_window_visible)
+ gtk_widget_show (app->screenshot_window);
+ else
+ gtk_widget_hide (app->screenshot_window);
+
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (app->screenshot_item),
+ app->screenshot_window_visible);
+
+ show_samples (app);
}
static void
-set_busy (GtkWidget *widget, gboolean busy)
+set_busy (GtkWidget *widget,
+ gboolean busy)
{
GdkCursor *cursor;
-
+ GdkWindow *window;
+
if (busy)
cursor = gdk_cursor_new (GDK_WATCH);
else
cursor = NULL;
- gdk_window_set_cursor (widget->window, cursor);
-
- if (cursor)
- gdk_cursor_unref (cursor);
-
- gdk_flush ();
-}
-
-#if 0
-static gchar *
-get_name (pid_t pid)
-{
- char *cmdline;
- char *name = g_strdup_printf ("/proc/%d/cmdline", pid);
-
- if (g_file_get_contents (name, &cmdline, NULL, NULL))
- return cmdline;
+ if (GTK_IS_TEXT_VIEW (widget))
+ window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT);
else
- return g_strdup ("<unknown>");
-}
-#endif
+ window = widget->window;
-#if 0
-static void
-on_timeout (gpointer data)
-{
- Application *app = data;
- GList *pids, *list;
- int mypid = getpid();
-
- pids = list_processes ();
-
- for (list = pids; list != NULL; list = list->next)
- {
- int pid = GPOINTER_TO_INT (list->data);
-
- if (pid == mypid)
- continue;
-
- if (get_process_state (pid) == PROCESS_RUNNING)
- {
- Process *process;
- SysprofStackTrace trace;
- int i;
-
- if (!generate_stack_trace (pid, &trace))
- {
- continue;
- }
-
- process = process_get_from_pid (pid);
-
-#if 0
- process_ensure_map (process, trace.pid,
- (gulong)trace.addresses[i]);
-#endif
-
- g_print ("n addr: %d\n", trace.n_addresses);
- 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);
- g_list_free (pids);
-
- return TRUE;
-}
-#endif
-
-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
-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; */
+ gdk_window_set_cursor (window, cursor);
- 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);
+ if (cursor)
+ gdk_cursor_unref (cursor);
}
static void
@@ -405,12 +281,12 @@ set_application_title (Application *app,
new_name = g_path_get_basename (name);
else
new_name = NULL;
-
+
if (app->loaded_profile)
g_free (app->loaded_profile);
app->loaded_profile = new_name;
-
+
if (app->loaded_profile)
{
gtk_window_set_title (GTK_WINDOW (app->main_window),
@@ -419,7 +295,7 @@ set_application_title (Application *app,
else
{
gtk_window_set_title (GTK_WINDOW (app->main_window),
- "System Profiler");
+ APPLICATION_NAME);
}
}
@@ -430,51 +306,15 @@ delete_data (Application *app)
{
profile_free (app->profile);
app->profile = NULL;
-
+
gtk_tree_view_set_model (GTK_TREE_VIEW (app->object_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);
}
-
- if (app->stash)
- stack_stash_free (app->stash);
- app->stash = stack_stash_new ();
- process_flush_caches ();
- app->n_samples = 0;
- queue_show_samples (app);
- app->profile_from_file = FALSE;
- 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 */
-}
+ collector_reset (app->collector);
-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;
+ set_application_title (app, NULL);
}
static void
@@ -485,44 +325,23 @@ sorry (GtkWidget *parent_window,
va_list args;
char *message;
GtkWidget *dialog;
-
+
va_start (args, format);
g_vasprintf (&message, format, args);
va_end (args);
-
+
dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
- GTK_BUTTONS_OK, message);
+ GTK_BUTTONS_OK, "%s", message);
g_free (message);
-
+
gtk_window_set_title (GTK_WINDOW (dialog), APPLICATION_NAME " Warning");
-
+
gtk_dialog_run (GTK_DIALOG (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
on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button)
{
@@ -536,46 +355,29 @@ static void
on_start_toggled (GtkWidget *widget, gpointer data)
{
Application *app = data;
+ GError *err = NULL;
if (!gtk_toggle_tool_button_get_active (
GTK_TOGGLE_TOOL_BUTTON (app->start_button)))
+ {
return;
+ }
- if (app->input_fd == -1)
+ if (collector_start (app->collector, &err))
{
- int fd;
+ delete_data (app);
- fd = open ("/proc/sysprof-trace", O_RDONLY);
- 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->state = PROFILING;
+ }
+ else
+ {
+ sorry (app->main_window, err->message);
- app->input_fd = fd;
- fd_add_watch (app->input_fd, app);
+ g_error_free (err);
}
-
- fd_set_read_callback (app->input_fd, on_read);
-
- delete_data (app);
-
- g_idle_add_full (G_PRIORITY_LOW, start_profiling, app, NULL);
+
+ update_screenshot_window (app);
+ update_sensitivity (app);
}
enum
@@ -598,22 +400,18 @@ enum
{
DESCENDANTS_NAME,
DESCENDANTS_SELF,
- DESCENDANTS_NON_RECURSE,
- DESCENDANTS_TOTAL,
+ DESCENDANTS_CUMULATIVE,
DESCENDANTS_OBJECT
};
-static ProfileObject *
+static char *
get_current_object (Application *app)
{
- GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter selected;
- ProfileObject *object;
-
- selection = gtk_tree_view_get_selection (app->object_view);
-
- if (gtk_tree_selection_get_selected (selection, &model, &selected))
+ char *object;
+
+ if (gtk_tree_selection_get_selected (app->object_selection, &model, &selected))
{
gtk_tree_model_get (model, &selected,
OBJECT_OBJECT, &object,
@@ -633,77 +431,71 @@ fill_main_list (Application *app)
GtkListStore *list_store;
Profile *profile = app->profile;
GList *objects;
-
+
if (profile)
{
- gpointer sort_state;
-
list_store = gtk_list_store_new (4,
G_TYPE_STRING,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE,
G_TYPE_POINTER);
-
+
objects = profile_get_objects (profile);
for (list = objects; list != NULL; list = list->next)
{
ProfileObject *object = list->data;
GtkTreeIter iter;
double profile_size = profile_get_size (profile);
-
+
gtk_list_store_append (list_store, &iter);
-
+
gtk_list_store_set (list_store, &iter,
OBJECT_NAME, object->name,
OBJECT_SELF, 100.0 * object->self / profile_size,
OBJECT_TOTAL, 100.0 * object->total / profile_size,
- OBJECT_OBJECT, object,
+#if 0
+ OBJECT_SELF, (double)object->self,
+ OBJECT_TOTAL, (double)object->total,
+#endif
+ OBJECT_OBJECT, object->name,
-1);
}
+ g_list_foreach (objects, (GFunc)g_free, NULL);
g_list_free (objects);
-
- sort_state = save_sort_state (app->object_view);
-
- gtk_tree_view_set_model (app->object_view, GTK_TREE_MODEL (list_store));
-
- if (sort_state)
- {
- restore_sort_state (app->object_view, sort_state);
- }
- else
- {
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
- OBJECT_TOTAL,
- GTK_SORT_DESCENDING);
- }
-
+
+ tree_view_set_model_with_default_sort (app->object_view, GTK_TREE_MODEL (list_store),
+ OBJECT_TOTAL, GTK_SORT_DESCENDING);
+
g_object_unref (G_OBJECT (list_store));
}
-
+
gtk_tree_view_columns_autosize (app->object_view);
}
static void
-add_node (GtkTreeStore *store,
+add_node (FooTreeStore *store,
int size,
const GtkTreeIter *parent,
ProfileDescendant *node)
{
GtkTreeIter iter;
-
+
if (!node)
return;
- gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
+ foo_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
- gtk_tree_store_set (store, &iter,
- DESCENDANTS_NAME, node->object->name,
+ foo_tree_store_set (store, &iter,
+ DESCENDANTS_NAME, node->name,
DESCENDANTS_SELF, 100 * (node->self)/(double)size,
- DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
- DESCENDANTS_TOTAL, 100 * (node->total)/(double)size,
- DESCENDANTS_OBJECT, node->object,
+ DESCENDANTS_CUMULATIVE, 100 * (node->cumulative)/(double)size,
+#if 0
+ DESCENDANTS_SELF, (double)node->self,
+ DESCENDANTS_CUMULATIVE, (double)node->non_recursion,
+#endif
+ DESCENDANTS_OBJECT, node->name,
-1);
-
+
add_node (store, size, parent, node->siblings);
add_node (store, size, &iter, node->children);
}
@@ -711,28 +503,24 @@ add_node (GtkTreeStore *store,
static void
fill_descendants_tree (Application *app)
{
- GtkTreeStore *tree_store;
- gpointer sort_state;
-
- sort_state = save_sort_state (app->descendants_view);
-
+ FooTreeStore *tree_store;
+
if (app->descendants)
{
profile_descendant_free (app->descendants);
app->descendants = NULL;
}
-
+
tree_store =
- gtk_tree_store_new (5,
+ foo_tree_store_new (4,
G_TYPE_STRING,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE,
- G_TYPE_DOUBLE,
G_TYPE_POINTER);
-
+
if (app->profile)
{
- ProfileObject *object = get_current_object (app);
+ char *object = get_current_object (app);
if (object)
{
app->descendants =
@@ -741,29 +529,18 @@ fill_descendants_tree (Application *app)
profile_get_size (app->profile), NULL, app->descendants);
}
}
-
- gtk_tree_view_set_model (
- app->descendants_view, GTK_TREE_MODEL (tree_store));
-
+
+ tree_view_set_model_with_default_sort (app->descendants_view, GTK_TREE_MODEL (tree_store),
+ DESCENDANTS_CUMULATIVE, GTK_SORT_DESCENDING);
+
g_object_unref (G_OBJECT (tree_store));
-
- if (!sort_state)
- {
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
- DESCENDANTS_NON_RECURSE,
- GTK_SORT_DESCENDING);
- }
- else
- {
- restore_sort_state (app->descendants_view, sort_state);
- }
-
+
gtk_tree_view_columns_autosize (app->descendants_view);
}
static void
-add_callers (GtkListStore *list_store,
- Profile *profile,
+add_callers (GtkListStore *list_store,
+ Profile *profile,
ProfileCaller *callers)
{
while (callers)
@@ -771,21 +548,25 @@ add_callers (GtkListStore *list_store,
gchar *name;
GtkTreeIter iter;
double profile_size = profile_get_size (profile);
-
- if (callers->object)
- name = callers->object->name;
+
+ if (callers->name)
+ name = callers->name;
else
name = "<spontaneous>";
-
+
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (
list_store, &iter,
CALLERS_NAME, name,
CALLERS_SELF, 100.0 * callers->self / profile_size,
CALLERS_TOTAL, 100.0 * callers->total / profile_size,
- CALLERS_OBJECT, callers->object,
+#if 0
+ CALLERS_SELF, (double)callers->self,
+ CALLERS_TOTAL, (double)callers->total,
+#endif
+ CALLERS_OBJECT, callers->name,
-1);
-
+
callers = callers->next;
}
}
@@ -794,58 +575,55 @@ static void
fill_callers_list (Application *app)
{
GtkListStore *list_store;
- gpointer sort_state;
-
- sort_state = save_sort_state (app->descendants_view);
-
+
if (app->callers)
{
profile_caller_free (app->callers);
app->callers = NULL;
}
-
+
list_store =
gtk_list_store_new (4,
G_TYPE_STRING,
G_TYPE_DOUBLE,
G_TYPE_DOUBLE,
G_TYPE_POINTER);
-
+
if (app->profile)
{
- ProfileObject *object = get_current_object (app);
+ char *object = get_current_object (app);
if (object)
{
app->callers = profile_list_callers (app->profile, object);
add_callers (list_store, app->profile, app->callers);
}
}
-
- gtk_tree_view_set_model (
- app->callers_view, GTK_TREE_MODEL (list_store));
-
+
+ tree_view_set_model_with_default_sort (app->callers_view, GTK_TREE_MODEL (list_store),
+ CALLERS_TOTAL, GTK_SORT_DESCENDING);
+
g_object_unref (G_OBJECT (list_store));
-
- if (!sort_state)
- {
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
- CALLERS_TOTAL,
- GTK_SORT_DESCENDING);
- }
- else
- {
- restore_sort_state (app->callers_view, sort_state);
- }
-
+
gtk_tree_view_columns_autosize (app->callers_view);
}
static void
-fill_lists (Application *app)
+enter_display_mode (Application *app)
{
+ app->state = DISPLAYING;
+
+ update_sensitivity (app);
+
+ app->inhibit_forced_redraw = TRUE;
+
fill_main_list (app);
- fill_callers_list (app);
- fill_descendants_tree (app);
+
+ /* This has the side effect of selecting the first row, which in turn causes
+ * the other lists to be filled out
+ */
+ gtk_widget_grab_focus (GTK_WIDGET (app->object_view));
+
+ app->inhibit_forced_redraw = FALSE;
}
static void
@@ -853,14 +631,14 @@ ensure_profile (Application *app)
{
if (app->profile)
return;
-
- app->profile = profile_new (app->stash);
- fill_lists (app);
-
- app->state = DISPLAYING;
-
- update_sensitivity (app);
+ collector_stop (app->collector);
+
+ app->profile = collector_create_profile (app->collector);
+
+ collector_reset (app->collector);
+
+ enter_display_mode (app);
}
static void
@@ -868,11 +646,17 @@ on_about_activated (GtkWidget *widget, gpointer data)
{
#define OSLASH "\303\270"
Application *app = data;
+ char *name_property;
+
+ if (gtk_minor_version >= 12)
+ name_property = "program-name";
+ else
+ name_property = "name";
gtk_show_about_dialog (GTK_WINDOW (app->main_window),
"logo", app->icon,
- "name", APPLICATION_NAME,
- "copyright", "Copyright 2004-2008, S"OSLASH"ren Sandmann",
+ name_property, APPLICATION_NAME,
+ "copyright", "Copyright 2004-2009, S"OSLASH"ren Sandmann",
"version", PACKAGE_VERSION,
NULL);
}
@@ -885,12 +669,6 @@ on_profile_toggled (GtkWidget *widget, gpointer data)
if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (app->profile_button)))
{
set_busy (app->main_window, TRUE);
- if (app->profile && !app->profile_from_file)
- {
- profile_free (app->profile);
- app->profile = NULL;
- }
-
ensure_profile (app);
set_busy (app->main_window, FALSE);
}
@@ -902,12 +680,15 @@ on_reset_clicked (gpointer widget, gpointer data)
Application *app = data;
set_busy (app->main_window, TRUE);
-
+
delete_data (app);
-
+
if (app->state == DISPLAYING)
+ {
app->state = INITIAL;
-
+ collector_stop (app->collector);
+ }
+
update_sensitivity (app);
set_busy (app->main_window, FALSE);
@@ -921,7 +702,7 @@ overwrite_file (GtkWindow *window,
gchar *utf8_file_name;
AtkObject *obj;
gint ret;
-
+
utf8_file_name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
msgbox = gtk_message_dialog_new (window,
(GtkDialogFlags)GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -930,30 +711,29 @@ overwrite_file (GtkWindow *window,
_("A file named \"%s\" already exists."),
utf8_file_name);
g_free (utf8_file_name);
-
+
gtk_message_dialog_format_secondary_text (
GTK_MESSAGE_DIALOG (msgbox),
_("Do you want to replace it with the one you are saving?"));
-
+
gtk_dialog_add_button (GTK_DIALOG (msgbox),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
-
+
gtk_dialog_add_button (GTK_DIALOG (msgbox),
_("_Replace"), GTK_RESPONSE_YES);
-
+
gtk_dialog_set_default_response (GTK_DIALOG (msgbox),
GTK_RESPONSE_CANCEL);
-
+
obj = gtk_widget_get_accessible (msgbox);
-
+
if (GTK_IS_ACCESSIBLE (obj))
atk_object_set_name (obj, _("Question"));
-
+
ret = gtk_dialog_run (GTK_DIALOG (msgbox));
gtk_widget_destroy (msgbox);
-
+
return (ret == GTK_RESPONSE_YES);
-
}
static void
@@ -962,38 +742,38 @@ on_save_as_clicked (gpointer widget,
{
Application *app = data;
GtkWidget *dialog;
-
+
ensure_profile (app);
-
+
set_busy (app->main_window, TRUE);
-
+
dialog = gtk_file_chooser_dialog_new ("Save As",
GTK_WINDOW (app->main_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
-
+
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-
+
set_busy (app->main_window, FALSE);
-
- retry:
+
+retry:
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
GError *err = NULL;
gchar *filename;
-
+
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-
+
if (g_file_test (filename, G_FILE_TEST_EXISTS) &&
!overwrite_file (GTK_WINDOW (app->main_window), filename))
{
g_free (filename);
goto retry;
}
-
+
set_busy (dialog, TRUE);
if (!profile_save (app->profile, filename, &err))
{
@@ -1008,7 +788,7 @@ on_save_as_clicked (gpointer widget,
set_busy (dialog, FALSE);
g_free (filename);
}
-
+
gtk_widget_destroy (dialog);
}
@@ -1019,25 +799,16 @@ set_loaded_profile (Application *app,
{
g_return_if_fail (name != NULL);
g_return_if_fail (profile != NULL);
-
- set_busy (app->main_window, TRUE);
-
+
+ collector_stop (app->collector);
+
delete_data (app);
-
- app->state = DISPLAYING;
-
- app->n_samples = profile_get_size (profile);
-
+
app->profile = profile;
- app->profile_from_file = TRUE;
-
- fill_lists (app);
set_application_title (app, name);
-
- update_sensitivity (app);
-
- set_busy (app->main_window, FALSE);
+
+ enter_display_mode (app);
}
static void
@@ -1050,7 +821,7 @@ show_could_not_open (Application *app,
filename,
err->message);
}
-
+
static void
on_open_clicked (gpointer widget,
gpointer data)
@@ -1061,29 +832,29 @@ on_open_clicked (gpointer widget,
GtkWidget *dialog;
set_busy (app->main_window, TRUE);
-
+
dialog = gtk_file_chooser_dialog_new ("Open",
GTK_WINDOW (app->main_window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
-
+
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
set_busy (app->main_window, FALSE);
-
- retry:
+
+retry:
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
GError *err = NULL;
-
+
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
set_busy (dialog, TRUE);
profile = profile_load (filename, &err);
-
+
if (!profile)
{
set_busy (dialog, FALSE);
@@ -1091,11 +862,11 @@ on_open_clicked (gpointer widget,
show_could_not_open (app, filename, err);
g_error_free (err);
g_free (filename);
-
+
filename = NULL;
goto retry;
}
-
+
set_busy (dialog, FALSE);
}
@@ -1105,14 +876,23 @@ on_open_clicked (gpointer widget,
{
g_assert (filename);
set_loaded_profile (app, filename, profile);
-
+
g_free (filename);
}
}
static void
-on_delete (GtkWidget *window)
+on_delete (GtkWidget *window,
+ Application *app)
{
+ /* Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=317775
+ *
+ * Without it, the read callbacks can fire _after_ gtk_main_quit()
+ * has been called and cause stuff to be called on destroyed widgets.
+ */
+ while (gtk_main_iteration ())
+ ;
+
gtk_main_quit ();
}
@@ -1138,7 +918,7 @@ expand_descendants_tree (Application *app)
gtk_tree_model_get (model, &iter,
OBJECT_TOTAL, &top_value,
-1);
-
+
while (all_paths && n_rows < max_rows)
{
GtkTreeIter best_iter;
@@ -1154,31 +934,33 @@ expand_descendants_tree (Application *app)
{
GtkTreePath *path = list->data;
GtkTreeIter iter;
- double value;
+
g_assert (path != NULL);
+
if (gtk_tree_model_get_iter (model, &iter, path))
{
+ double value;
gtk_tree_model_get (model, &iter,
OBJECT_TOTAL, &value,
-1);
- }
- if (value >= best_value)
- {
- best_value = value;
- best_path = path;
- gtk_tree_model_get_iter (model, &best_iter, path);
+ if (value >= best_value)
+ {
+ best_value = value;
+ best_path = path;
+ best_iter = iter;
+ }
}
}
- gtk_tree_model_get_iter (model, &iter, best_path);
-
n_children = gtk_tree_model_iter_n_children (model, &best_iter);
-
+
if (n_children && (best_value / top_value) > 0.04 &&
- (n_children + gtk_tree_path_get_depth (best_path)) / (double)max_rows < (best_value / top_value) )
+ (n_children + gtk_tree_path_get_depth (best_path)) /
+ (double)max_rows < (best_value / top_value) )
{
- gtk_tree_view_expand_row (GTK_TREE_VIEW (app->descendants_view), best_path, FALSE);
+ gtk_tree_view_expand_row (
+ GTK_TREE_VIEW (app->descendants_view), best_path, FALSE);
n_rows += n_children;
if (gtk_tree_path_get_depth (best_path) < 4)
@@ -1193,60 +975,239 @@ expand_descendants_tree (Application *app)
path = gtk_tree_path_copy (path);
gtk_tree_path_next (path);
}
+
gtk_tree_path_free (path);
}
}
all_paths = g_list_remove (all_paths, best_path);
+
+ if (!all_paths && n_rows == 1)
+ {
+ /* Always expand at least once */
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (app->descendants_view),
+ best_path, FALSE);
+ }
+
gtk_tree_path_free (best_path);
}
for (list = all_paths; list != NULL; list = list->next)
gtk_tree_path_free (list->data);
-
+
g_list_free (all_paths);
}
static void
+get_data (GtkTreeView *view,
+ GtkTreeIter *iter,
+ gchar **name,
+ double *self,
+ double *cumulative)
+{
+ char *dummy1;
+ double dummy2;
+ double dummy3;
+
+ GtkTreeModel *model = gtk_tree_view_get_model (view);
+ gtk_tree_model_get (
+ model, iter,
+ DESCENDANTS_NAME, name? name : &dummy1,
+ DESCENDANTS_SELF, self? self : &dummy2,
+ DESCENDANTS_CUMULATIVE, cumulative? cumulative : &dummy3,
+ -1);
+}
+
+static int
+get_indent (GtkTreePath *path)
+{
+ return 2 * (gtk_tree_path_get_depth (path) - 1);
+}
+
+static void
+compute_text_width (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ int *width = data;
+ char *name;
+
+ get_data (view, iter, &name, NULL, NULL);
+
+ *width = MAX (g_utf8_strlen (name, -1) + get_indent (path), *width);
+
+ g_free (name);
+}
+
+typedef struct
+{
+ int max_width;
+ GString *text;
+} AddTextInfo;
+
+static void
+set_monospace (GtkWidget *widget)
+{
+ PangoFontDescription *desc =
+ pango_font_description_from_string ("monospace");
+
+ gtk_widget_modify_font (widget, desc);
+
+ pango_font_description_free (desc);
+}
+
+static void
+add_text (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ AddTextInfo *info = data;
+ char *name;
+ double self;
+ double cumulative;
+ int indent;
+ int i;
+
+ get_data (view, iter, &name, &self, &cumulative);
+
+ indent = get_indent (path);
+
+ for (i = 0; i < indent; ++i)
+ g_string_append_c (info->text, ' ');
+
+ g_string_append_printf (info->text, "%-*s %6.2f%% %6.2f%%\n",
+ info->max_width - indent, name, self, cumulative);
+
+ g_free (name);
+}
+
+static gboolean
+update_screenshot_window_idle (gpointer data)
+{
+ Application *app = data;
+ GtkTextBuffer *text_buffer;
+
+ if (!app->screenshot_window_visible)
+ return FALSE;
+
+ text_buffer =
+ gtk_text_view_get_buffer (GTK_TEXT_VIEW (app->screenshot_textview));
+
+ gtk_text_buffer_set_text (text_buffer, "", -1);
+
+ if (app->descendants)
+ {
+ AddTextInfo info;
+
+ info.max_width = 0;
+ info.text = g_string_new ("");
+
+ tree_view_foreach_visible (app->descendants_view,
+ compute_text_width,
+ &info.max_width);
+
+ tree_view_foreach_visible (app->descendants_view,
+ add_text,
+ &info);
+
+ gtk_text_buffer_set_text (text_buffer, info.text->str, -1);
+
+ set_monospace (app->screenshot_textview);
+
+ g_string_free (info.text, TRUE);
+ }
+
+ app->update_screenshot_id = 0;
+
+ if (app->screenshot_window_visible)
+ {
+ set_busy (app->screenshot_window, FALSE);
+ set_busy (app->screenshot_textview, FALSE);
+ }
+
+ return FALSE;
+}
+
+static void
+update_screenshot_window (Application *app)
+{
+ /* We do this in an idle handler to deal with the case where
+ * someone presses Shift-RightArrow on the root of a huge
+ * profile. This causes a ton of 'expanded' notifications,
+ * each of which would cause us to traverse the tree and
+ * update the screenshot window.
+ */
+ if (app->update_screenshot_id)
+ g_source_remove (app->update_screenshot_id);
+
+ if (app->screenshot_window_visible)
+ {
+ /* don't swamp the X server with cursor change requests */
+ if (!app->update_screenshot_id)
+ {
+ set_busy (app->screenshot_window, TRUE);
+ set_busy (app->screenshot_textview, TRUE);
+ }
+ }
+
+ app->update_screenshot_id = g_idle_add (
+ update_screenshot_window_idle, app);
+}
+
+static void
+on_descendants_row_expanded_or_collapsed (GtkTreeView *tree,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ Application *app)
+{
+ update_screenshot_window (app);
+}
+
+static void
on_object_selection_changed (GtkTreeSelection *selection,
- gpointer data)
+ gpointer data)
{
Application *app = data;
set_busy (app->main_window, TRUE);
- gdk_window_process_all_updates ();
-
+ update_screenshot_window (app);
+
+ if (!app->inhibit_forced_redraw)
+ gdk_window_process_all_updates (); /* Display updated selection */
+
fill_descendants_tree (app);
fill_callers_list (app);
if (get_current_object (app))
expand_descendants_tree (app);
-
+
set_busy (app->main_window, FALSE);
}
static void
really_goto_object (Application *app,
- ProfileObject *object)
+ char *object)
{
GtkTreeModel *profile_objects;
GtkTreeIter iter;
gboolean found = FALSE;
-
+
profile_objects = gtk_tree_view_get_model (app->object_view);
-
+
if (gtk_tree_model_get_iter_first (profile_objects, &iter))
{
do
{
- ProfileObject *profile_object;
-
+ char *list_object;
+
gtk_tree_model_get (profile_objects, &iter,
- OBJECT_OBJECT, &profile_object,
+ OBJECT_OBJECT, &list_object,
-1);
-
- if (profile_object == object)
+
+ if (list_object == object)
{
found = TRUE;
break;
@@ -1254,12 +1215,12 @@ really_goto_object (Application *app,
}
while (gtk_tree_model_iter_next (profile_objects, &iter));
}
-
+
if (found)
{
GtkTreePath *path =
gtk_tree_model_get_path (profile_objects, &iter);
-
+
gtk_tree_view_set_cursor (app->object_view, path, 0, FALSE);
}
}
@@ -1272,18 +1233,17 @@ goto_object (Application *app,
{
GtkTreeIter iter;
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
- ProfileObject *object;
-
+ char *object;
+
if (!gtk_tree_model_get_iter (model, &iter, path))
return;
-
+
gtk_tree_model_get (model, &iter, column, &object, -1);
-
+
if (!object)
return;
-
+
really_goto_object (app, object);
-
}
static void
@@ -1293,9 +1253,9 @@ on_descendants_row_activated (GtkTreeView *tree_view,
gpointer data)
{
Application *app = data;
-
+
goto_object (app, tree_view, path, DESCENDANTS_OBJECT);
-
+
gtk_widget_grab_focus (GTK_WIDGET (app->descendants_view));
}
@@ -1306,14 +1266,45 @@ on_callers_row_activated (GtkTreeView *tree_view,
gpointer data)
{
Application *app = data;
-
+
goto_object (app, tree_view, path, CALLERS_OBJECT);
gtk_widget_grab_focus (GTK_WIDGET (app->callers_view));
}
static void
+on_screenshot_activated (GtkCheckMenuItem *menu_item,
+ Application *app)
+{
+ app->screenshot_window_visible = gtk_check_menu_item_get_active (menu_item);
+
+ update_screenshot_window (app);
+
+ update_sensitivity (app);
+}
+
+static void
+on_screenshot_window_delete (GtkWidget *window,
+ GdkEvent *event,
+ Application *app)
+{
+ app->screenshot_window_visible = FALSE;
+
+ update_sensitivity (app);
+}
+
+static void
+on_screenshot_close_button_clicked (GtkWidget *widget,
+ Application *app)
+{
+ app->screenshot_window_visible = FALSE;
+
+ update_sensitivity (app);
+}
+
+static void
set_sizes (GtkWindow *window,
+ GtkWindow *screenshot_window,
GtkWidget *hpaned,
GtkWidget *vpaned)
{
@@ -1322,19 +1313,123 @@ set_sizes (GtkWindow *window,
GdkRectangle monitor;
int width, height;
GtkWidget *widget = GTK_WIDGET (window);
-
+
screen = gtk_widget_get_screen (widget);
monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
-
+
gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
+
width = monitor.width * 3 / 4;
height = monitor.height * 3 / 4;
-
+
gtk_window_resize (window, width, height);
-
+
gtk_paned_set_position (GTK_PANED (vpaned), height / 2);
- gtk_paned_set_position (GTK_PANED (hpaned), width / 2);
+ gtk_paned_set_position (GTK_PANED (hpaned), width * 3 / 8);
+
+ width = monitor.width * 5 / 8;
+ height = monitor.height * 5 / 8;
+
+ gtk_window_resize (screenshot_window, width, height);
+}
+
+#define GLADE_FILE DATADIR "/sysprof.glade"
+
+static void
+gather_widgets (Application *app)
+{
+ typedef struct
+ {
+ void *location;
+ const char *name;
+ } WidgetInfo;
+
+ const WidgetInfo widgets[] =
+ {
+ { &app->main_window, "main_window" },
+ { &app->start_button, "start_button" },
+ { &app->profile_button, "profile_button" },
+ { &app->reset_button, "reset_button" },
+ { &app->save_as_button, "save_as_button" },
+ { &app->dummy_button, "dummy_button" },
+ { &app->samples_label, "samples_label" },
+ { &app->samples_hbox, "samples_hbox" },
+ { &app->start_item, "start_item" },
+ { &app->profile_item, "profile_item" },
+ { &app->reset_item, "reset_item" },
+ { &app->open_item, "open_item" },
+ { &app->save_as_item, "save_as_item" },
+ { &app->screenshot_item, "screenshot_item" },
+ { &app->quit_item, "quit" },
+ { &app->about_item, "about" },
+ { &app->object_view, "object_view" },
+ { &app->callers_view, "callers_view" },
+ { &app->descendants_view, "descendants_view" },
+ { &app->screenshot_window, "screenshot_window" },
+ { &app->screenshot_textview, "screenshot_textview" },
+ { &app->screenshot_close_button, "screenshot_close_button" },
+ { &app->vpaned, "vpaned" },
+ { &app->hpaned, "hpaned" },
+ };
+
+ GladeXML *xml = glade_xml_new (GLADE_FILE, NULL, NULL);
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (widgets); ++i)
+ {
+ const WidgetInfo *info = &(widgets[i]);
+
+ *(GtkWidget **)(info->location) = glade_xml_get_widget (xml, info->name);
+
+ g_assert (GTK_IS_WIDGET (*(GtkWidget **)info->location));
+ }
+
+ g_object_unref (xml);
+}
+
+static void
+connect_signals (Application *app)
+{
+ typedef struct
+ {
+ gpointer object;
+ const char *signal;
+ gpointer callback;
+ gpointer data;
+ } SignalInfo;
+
+ const SignalInfo signals[] =
+ {
+ { app->main_window, "delete_event", on_delete, NULL },
+ { app->start_button, "toggled", on_start_toggled, app },
+ { app->profile_button, "toggled", on_profile_toggled, app },
+ { app->reset_button, "clicked", on_reset_clicked, app },
+ { app->save_as_button, "clicked", on_save_as_clicked, app },
+ { app->start_item, "activate", on_menu_item_activated, app->start_button },
+ { app->profile_item, "activate", on_menu_item_activated, app->profile_button },
+ { app->reset_item, "activate", on_reset_clicked, app },
+ { app->open_item, "activate", on_open_clicked, app },
+ { app->save_as_item, "activate", on_save_as_clicked, app },
+ { app->screenshot_item, "activate", on_screenshot_activated, app },
+ { app->quit_item, "activate", on_delete, NULL },
+ { app->about_item, "activate", on_about_activated, app },
+ { app->object_selection, "changed", on_object_selection_changed, app },
+ { app->callers_view, "row-activated", on_callers_row_activated, app },
+ { app->descendants_view, "row-activated", on_descendants_row_activated, app },
+ { app->descendants_view, "row-expanded", on_descendants_row_expanded_or_collapsed, app },
+ { app->descendants_view, "row-collapsed", on_descendants_row_expanded_or_collapsed, app },
+ { app->screenshot_window, "delete_event", on_screenshot_window_delete, app },
+ { app->screenshot_close_button, "clicked", on_screenshot_close_button_clicked, app },
+ };
+
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (signals); ++i)
+ {
+ const SignalInfo *info = &(signals[i]);
+
+ g_signal_connect (info->object, info->signal, info->callback, info->data);
+ }
}
static void
@@ -1354,140 +1449,147 @@ set_shadows (void)
}
static void
+set_icons (Application *app)
+{
+ const char *icon_files [] = {
+ PIXMAPDIR "/sysprof-icon-16.png",
+ PIXMAPDIR "/sysprof-icon-24.png",
+ PIXMAPDIR "/sysprof-icon-32.png",
+ PIXMAPDIR "/sysprof-icon-48.png",
+ NULL
+ };
+ GList *pixbufs = NULL;
+ int i;
+
+ for (i = 0; icon_files[i] != NULL; ++i)
+ {
+ const char *file = icon_files[i];
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (file, NULL);
+
+ if (pixbuf)
+ {
+ pixbufs = g_list_prepend (pixbufs, pixbuf);
+
+ if (i == 3) /* 48 x 48 */
+ app->icon = g_object_ref (pixbuf);
+ }
+ else
+ {
+ g_warning ("Could not open %s\n", file);
+ }
+ }
+
+ gtk_window_set_icon_list (GTK_WINDOW (app->main_window), pixbufs);
+
+ g_list_foreach (pixbufs, (GFunc)g_object_unref, NULL);
+ g_list_free (pixbufs);
+}
+
+#define PCT_FORMAT "%.2f<span size='smaller'><span size='smaller'> </span>%%</span>"
+
+static gboolean
build_gui (Application *app)
{
- GladeXML *xml;
- GtkTreeSelection *selection;
GtkTreeViewColumn *col;
set_shadows ();
-
- xml = glade_xml_new (DATADIR "/sysprof.glade", NULL, NULL);
-
+
+ if (!g_file_test (GLADE_FILE, G_FILE_TEST_EXISTS))
+ {
+ sorry (NULL,
+ "Sysprof was not compiled or installed correctly.\n"
+ "\n"
+ "Running \"make install\" may solve this problem.\n");
+
+ return FALSE;
+ }
+
+ gather_widgets (app);
+
+ g_assert (app->main_window);
+
/* Main Window */
- app->main_window = glade_xml_get_widget (xml, "main_window");
- app->icon = gdk_pixbuf_new_from_file (PIXMAPDIR "/sysprof-icon.png", NULL);
+ set_icons (app);
- gtk_window_set_icon (GTK_WINDOW (app->main_window), app->icon);
-
- g_signal_connect (G_OBJECT (app->main_window), "delete_event",
- G_CALLBACK (on_delete), NULL);
-
gtk_widget_realize (GTK_WIDGET (app->main_window));
- set_sizes (GTK_WINDOW (app->main_window),
- glade_xml_get_widget (xml, "hpaned"),
- glade_xml_get_widget (xml, "vpaned"));
-
+
/* Tool items */
-
- app->start_button = glade_xml_get_widget (xml, "start_button");
- app->profile_button = glade_xml_get_widget (xml, "profile_button");
- app->reset_button = glade_xml_get_widget (xml, "reset_button");
- app->save_as_button = glade_xml_get_widget (xml, "save_as_button");
- app->dummy_button = glade_xml_get_widget (xml, "dummy_button");
-
gtk_toggle_tool_button_set_active (
GTK_TOGGLE_TOOL_BUTTON (app->profile_button), FALSE);
-
- g_signal_connect (G_OBJECT (app->start_button), "toggled",
- G_CALLBACK (on_start_toggled), app);
-
- g_signal_connect (G_OBJECT (app->profile_button), "toggled",
- G_CALLBACK (on_profile_toggled), app);
-
- g_signal_connect (G_OBJECT (app->reset_button), "clicked",
- G_CALLBACK (on_reset_clicked), app);
-
- g_signal_connect (G_OBJECT (app->save_as_button), "clicked",
- G_CALLBACK (on_save_as_clicked), app);
-
-
- app->samples_label = glade_xml_get_widget (xml, "samples_label");
-
- /* Menu items */
- app->start_item = glade_xml_get_widget (xml, "start_item");
- app->profile_item = glade_xml_get_widget (xml, "profile_item");
- app->reset_item = glade_xml_get_widget (xml, "reset_item");
- app->open_item = glade_xml_get_widget (xml, "open_item");
- app->save_as_item = glade_xml_get_widget (xml, "save_as_item");
-
- g_assert (app->start_item);
- g_assert (app->profile_item);
- g_assert (app->save_as_item);
- g_assert (app->open_item);
-
- g_signal_connect (G_OBJECT (app->start_item), "activate",
- G_CALLBACK (on_menu_item_activated), app->start_button);
-
- g_signal_connect (G_OBJECT (app->profile_item), "activate",
- G_CALLBACK (on_menu_item_activated), app->profile_button);
-
- g_signal_connect (G_OBJECT (app->reset_item), "activate",
- G_CALLBACK (on_reset_clicked), app);
-
- g_signal_connect (G_OBJECT (app->open_item), "activate",
- G_CALLBACK (on_open_clicked), app);
-
- g_signal_connect (G_OBJECT (app->save_as_item), "activate",
- G_CALLBACK (on_save_as_clicked), app);
-
- g_signal_connect (G_OBJECT (glade_xml_get_widget (xml, "quit")), "activate",
- G_CALLBACK (on_delete), NULL);
- g_signal_connect (G_OBJECT (glade_xml_get_widget (xml, "about")), "activate",
- G_CALLBACK (on_about_activated), app);
-
/* TreeViews */
-
+
/* object view */
- app->object_view = (GtkTreeView *)glade_xml_get_widget (xml, "object_view");
gtk_tree_view_set_enable_search (app->object_view, FALSE);
- col = add_plain_text_column (app->object_view, _("Functions"), OBJECT_NAME);
- add_double_format_column (app->object_view, _("Self"), OBJECT_SELF, "%.2f ");
- add_double_format_column (app->object_view, _("Total"), OBJECT_TOTAL, "%.2f ");
- selection = gtk_tree_view_get_selection (app->object_view);
- g_signal_connect (selection, "changed", G_CALLBACK (on_object_selection_changed), app);
+ col = add_plain_text_column (app->object_view, _("Functions"),
+ OBJECT_NAME);
+ add_double_format_column (app->object_view, _("Self"),
+ OBJECT_SELF, PCT_FORMAT);
+ add_double_format_column (app->object_view, _("Total"),
+ OBJECT_TOTAL, PCT_FORMAT);
+ app->object_selection = gtk_tree_view_get_selection (app->object_view);
gtk_tree_view_column_set_expand (col, TRUE);
-
+
/* callers view */
- app->callers_view = (GtkTreeView *)glade_xml_get_widget (xml, "callers_view");
gtk_tree_view_set_enable_search (app->callers_view, FALSE);
- col = add_plain_text_column (app->callers_view, _("Callers"), CALLERS_NAME);
- add_double_format_column (app->callers_view, _("Self"), CALLERS_SELF, "%.2f ");
- add_double_format_column (app->callers_view, _("Total"), CALLERS_TOTAL, "%.2f ");
- g_signal_connect (app->callers_view, "row-activated",
- G_CALLBACK (on_callers_row_activated), app);
+ col = add_plain_text_column (app->callers_view, _("Callers"),
+ CALLERS_NAME);
+ add_double_format_column (app->callers_view, _("Self"),
+ CALLERS_SELF, PCT_FORMAT);
+ add_double_format_column (app->callers_view, _("Total"),
+ CALLERS_TOTAL, PCT_FORMAT);
gtk_tree_view_column_set_expand (col, TRUE);
-
+
/* descendants view */
- app->descendants_view = (GtkTreeView *)glade_xml_get_widget (xml, "descendants_view");
gtk_tree_view_set_enable_search (app->descendants_view, FALSE);
- col = add_plain_text_column (app->descendants_view, _("Descendants"), DESCENDANTS_NAME);
- add_double_format_column (app->descendants_view, _("Self"), DESCENDANTS_SELF, "%.2f ");
- add_double_format_column (app->descendants_view, _("Cumulative"), DESCENDANTS_NON_RECURSE, "%.2f ");
- g_signal_connect (app->descendants_view, "row-activated",
- G_CALLBACK (on_descendants_row_activated), app);
+ col = add_plain_text_column (app->descendants_view, _("Descendants"),
+ DESCENDANTS_NAME);
+ add_double_format_column (app->descendants_view, _("Self"),
+ DESCENDANTS_SELF, PCT_FORMAT);
+ add_double_format_column (app->descendants_view, _("Cumulative"),
+ DESCENDANTS_CUMULATIVE, PCT_FORMAT);
gtk_tree_view_column_set_expand (col, TRUE);
-
- gtk_widget_grab_focus (GTK_WIDGET (app->object_view));
+
+ /* screenshot window */
+
+ /* set sizes */
+ set_sizes (GTK_WINDOW (app->main_window),
+ GTK_WINDOW (app->screenshot_window),
+ app->hpaned, app->vpaned);
+
+ /* hide/show widgets */
gtk_widget_show_all (app->main_window);
gtk_widget_hide (app->dummy_button);
-
- /* Statusbar */
+ gtk_widget_hide (app->screenshot_window);
+
+ gtk_widget_grab_focus (GTK_WIDGET (app->object_view));
queue_show_samples (app);
+
+ connect_signals (app);
+
+ return TRUE;
+}
+
+static void
+on_new_sample (gboolean first_sample,
+ gpointer data)
+{
+ Application *app = data;
+
+ if (app->state == PROFILING && first_sample)
+ update_sensitivity (app);
+ else
+ queue_show_samples (app);
}
static Application *
application_new (void)
{
Application *app = g_new0 (Application, 1);
-
- app->stash = stack_stash_new ();
- app->input_fd = -1;
+
+ app->collector = collector_new (FALSE, on_new_sample, app);
app->state = INITIAL;
- g_get_current_time (&app->latest_reset);
-
return app;
}
@@ -1507,53 +1609,128 @@ load_file (gpointer data)
Profile *profile;
set_busy (app->main_window, TRUE);
-
+
profile = profile_load (filename, &err);
-
- set_busy (app->main_window, FALSE);
-
+
if (profile)
{
set_loaded_profile (app, filename, profile);
+
+ gdk_window_process_all_updates ();
+ set_busy (app->main_window, FALSE);
}
else
{
+ set_busy (app->main_window, FALSE);
+
show_could_not_open (app, filename, err);
g_error_free (err);
}
+ g_free (file_open_data);
+
return FALSE;
}
+static const char *
+process_options (int argc,
+ char **argv)
+{
+ int i;
+ gboolean show_version = FALSE;
+ const char *filename = NULL;
+
+ for (i = 1; i < argc; ++i)
+ {
+ char *option = argv[i];
+
+ if (strcmp (option, "--version") == 0)
+ {
+ show_version = TRUE;
+ }
+ else if (!filename)
+ {
+ filename = argv[i];
+ }
+ }
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", APPLICATION_NAME, PACKAGE_VERSION);
+
+ exit (1);
+ }
+
+ return filename;
+}
+
+static void
+apply_workarounds (void)
+{
+
+ /* Disable gslice, since it
+ *
+ * - confuses valgrind
+ * - caches too much memory
+ * - hides memory access bugs
+ * - is not faster than malloc
+ *
+ * Note that g_slice_set_config() is broken in some versions of
+ * GLib (and 'declared internal' according to Tim), so we use the
+ * environment variable instead.
+ */
+ if (!getenv ("G_SLICE"))
+ putenv ("G_SLICE=always_malloc");
+
+ /* Accessibility prevents sysprof from working reliably, so
+ * disable it. Specifically, it
+ *
+ * - causes large amounts of time to be spent in sysprof itself
+ * whenever the label is updated.
+ * - sometimes hangs at shutdown
+ * - does long-running roundtrip requests that prevents
+ * reading the event buffers, resulting in lost events.
+ */
+ putenv ("NO_GAIL=1");
+ putenv ("NO_AT_BRIDGE=1");
+}
+
int
-main (int argc, char **argv)
+main (int argc,
+ char **argv)
{
Application *app;
-
+ const char *filename;
+
+ apply_workarounds();
+
+ filename = process_options (argc, argv);
+
gtk_init (&argc, &argv);
-
+
app = application_new ();
-
-#if 0
- nice (-19);
- g_timeout_add (10, on_timeout, app);
-#endif
-
- build_gui (app);
-
+
+ if (!build_gui (app))
+ return -1;
+
update_sensitivity (app);
- if (argc > 1)
+ if (filename)
{
FileOpenData *file_open_data = g_new0 (FileOpenData, 1);
-
- file_open_data->filename = argv[1];
+ file_open_data->filename = filename;
file_open_data->app = app;
- g_idle_add (load_file, file_open_data);
+ /* This has to run at G_PRIORITY_LOW because of bug 350517
+ */
+ g_idle_add_full (G_PRIORITY_LOW, load_file, file_open_data, NULL);
}
+#if 0
+ g_idle_add (gtk_main_quit, NULL);
+#endif
+
gtk_main ();
-
+
return 0;
}
diff --git a/sysprof.glade b/sysprof.glade
index c68756e..cf8c655 100644
--- a/sysprof.glade
+++ b/sysprof.glade
@@ -18,6 +18,7 @@
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
<child>
<widget class="GtkVBox" id="main_vbox">
@@ -28,28 +29,29 @@
<child>
<widget class="GtkMenuBar" id="menubar">
<property name="visible">True</property>
+ <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+ <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
<child>
<widget class="GtkMenuItem" id="menuitem1">
<property name="visible">True</property>
- <property name="label" translatable="yes">_File</property>
+ <property name="label" translatable="yes">_Profiler</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menuitem1_menu">
<child>
- <widget class="GtkImageMenuItem" id="open_item">
+ <widget class="GtkImageMenuItem" id="start_item">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Open...</property>
+ <property name="label" translatable="yes">_Start</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_open_item_activate" last_modification_time="Thu, 04 Nov 2004 21:01:03 GMT"/>
- <accelerator key="o" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <signal name="activate" handler="on_start1_activate" last_modification_time="Thu, 04 Nov 2004 18:51:54 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image80">
+ <widget class="GtkImage" id="image128">
<property name="visible">True</property>
- <property name="stock">gtk-open</property>
+ <property name="stock">gtk-media-play</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
@@ -61,17 +63,16 @@
</child>
<child>
- <widget class="GtkImageMenuItem" id="save_as_item">
+ <widget class="GtkImageMenuItem" id="profile_item">
<property name="visible">True</property>
- <property name="label" translatable="yes">Save _As...</property>
+ <property name="label" translatable="yes">_Profile</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_Save _As..._activate" last_modification_time="Wed, 31 Dec 2003 20:44:40 GMT"/>
- <accelerator key="s" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+ <signal name="activate" handler="on_profile1_activate" last_modification_time="Thu, 04 Nov 2004 18:51:54 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image81">
+ <widget class="GtkImage" id="image129">
<property name="visible">True</property>
- <property name="stock">gtk-save-as</property>
+ <property name="stock">gtk-justify-left</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
@@ -83,23 +84,16 @@
</child>
<child>
- <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
- <property name="visible">True</property>
- </widget>
- </child>
-
- <child>
- <widget class="GtkImageMenuItem" id="quit">
+ <widget class="GtkImageMenuItem" id="reset_item">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Quit</property>
+ <property name="label" translatable="yes">_Reset</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_quit_activate" last_modification_time="Wed, 31 Dec 2003 20:44:40 GMT"/>
- <accelerator key="q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ <signal name="activate" handler="on_reset_item_activate" last_modification_time="Fri, 05 Nov 2004 15:34:30 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image82">
+ <widget class="GtkImage" id="image130">
<property name="visible">True</property>
- <property name="stock">gtk-quit</property>
+ <property name="stock">gtk-clear</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
@@ -109,31 +103,25 @@
</child>
</widget>
</child>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menuitem3">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_View</property>
- <property name="use_underline">True</property>
-
- <child>
- <widget class="GtkMenu" id="menuitem3_menu">
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
<child>
- <widget class="GtkImageMenuItem" id="start_item">
+ <widget class="GtkImageMenuItem" id="open_item">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Start</property>
+ <property name="label" translatable="yes">_Open...</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_start1_activate" last_modification_time="Thu, 04 Nov 2004 18:51:54 GMT"/>
+ <signal name="activate" handler="on_open_item_activate" last_modification_time="Thu, 04 Nov 2004 21:01:03 GMT"/>
+ <accelerator key="o" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image83">
+ <widget class="GtkImage" id="image131">
<property name="visible">True</property>
- <property name="stock">gtk-media-play</property>
+ <property name="stock">gtk-open</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
@@ -145,16 +133,17 @@
</child>
<child>
- <widget class="GtkImageMenuItem" id="profile_item">
+ <widget class="GtkImageMenuItem" id="save_as_item">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Profile</property>
+ <property name="label" translatable="yes">Save _As...</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_profile1_activate" last_modification_time="Thu, 04 Nov 2004 18:51:54 GMT"/>
+ <signal name="activate" handler="on_Save _As..._activate" last_modification_time="Wed, 31 Dec 2003 20:44:40 GMT"/>
+ <accelerator key="s" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image84">
+ <widget class="GtkImage" id="image132">
<property name="visible">True</property>
- <property name="stock">gtk-justify-left</property>
+ <property name="stock">gtk-save-as</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
@@ -166,16 +155,23 @@
</child>
<child>
- <widget class="GtkImageMenuItem" id="reset_item">
+ <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
- <property name="label" translatable="yes">_Reset</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="quit">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Quit</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_reset_item_activate" last_modification_time="Fri, 05 Nov 2004 15:34:30 GMT"/>
+ <signal name="activate" handler="on_quit_activate" last_modification_time="Wed, 31 Dec 2003 20:44:40 GMT"/>
+ <accelerator key="q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image85">
+ <widget class="GtkImage" id="image133">
<property name="visible">True</property>
- <property name="stock">gtk-clear</property>
+ <property name="stock">gtk-quit</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
@@ -191,6 +187,30 @@
</child>
<child>
+ <widget class="GtkMenuItem" id="view1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_view1_activate" last_modification_time="Tue, 08 Nov 2005 04:06:22 GMT"/>
+
+ <child>
+ <widget class="GtkMenu" id="view1_menu">
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="screenshot_item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Screenshot Window</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="on_screenshot_item_activate" last_modification_time="Tue, 08 Nov 2005 00:40:14 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
<widget class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
<property name="label" translatable="yes">_Help</property>
@@ -207,7 +227,7 @@
<signal name="activate" handler="on_about_activate" last_modification_time="Wed, 31 Dec 2003 20:44:40 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image86">
+ <widget class="GtkImage" id="image134">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
<property name="icon_size">1</property>
@@ -396,22 +416,230 @@
<property name="is_important">False</property>
<child>
- <widget class="GtkLabel" id="samples_label">
+ <widget class="GtkNotebook" id="notebook1">
<property name="visible">True</property>
- <property name="label" translatable="yes">Samples: 0</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">6</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkHBox" id="samples_hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Samples: </property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkLabel" id="samples_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">0</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label6</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">888888</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label7</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label2</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="sizer_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Samples: 8888888</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">label3</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
</widget>
</child>
</widget>
@@ -562,4 +790,137 @@
</child>
</widget>
+<widget class="GtkWindow" id="screenshot_window">
+ <property name="title" translatable="yes">Screenshot</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_UTILITY</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">3</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="screenshot_textview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Screenshot&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="screenshot_close_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
</glade-interface>
diff --git a/testelf.c b/testelf.c
new file mode 100644
index 0000000..92d8801
--- /dev/null
+++ b/testelf.c
@@ -0,0 +1,70 @@
+#include <glib.h>
+#include "elfparser.h"
+
+const char *n;
+
+static void
+check (ElfParser *elf, gulong addr)
+{
+ const ElfSym *sym = elf_parser_lookup_symbol (elf, addr);
+
+ if (!sym)
+ {
+ g_print ("not found\n");
+ return;
+ }
+
+ n = elf_parser_get_sym_name (elf, sym);
+
+ g_print ("%p => ", (void *)addr);
+
+ if (sym)
+ {
+ g_print ("found: %s (%p)\n",
+ elf_parser_get_sym_name (elf, sym),
+ (void *)elf_parser_get_sym_address (elf, sym));
+ }
+ else
+ {
+ g_print ("not found\n");
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ ElfParser *elf;
+ const char *build_id;
+ const char *filename;
+
+ if (argc == 1)
+ filename = "/usr/lib/libgtk-x11-2.0.so";
+ else
+ filename = argv[1];
+
+ elf = elf_parser_new (filename, NULL);
+
+ if (!elf)
+ {
+ g_print ("NO ELF!!!!\n");
+ return -1;
+ }
+
+ build_id = elf_parser_get_build_id (elf);
+
+ g_print ("build ID: %s\n", build_id);
+
+ elf_parser_get_crc32 (elf);
+
+#if 0
+ for (i = 0; i < 5000000; ++i)
+#endif
+ {
+ elf_parser_get_crc32 (elf);
+ check (elf, 0x077c80f0 - (0x07787000 - 0)); /* gtk_about_dialog_set_artists (add - (map - offset)) */
+
+ check (elf, 0x077c80f0 - (0x07787000 - 0)); /* same (but in the middle of the function */
+ }
+ return 0;
+}
+
diff --git a/testunwind.c b/testunwind.c
new file mode 100644
index 0000000..c6b4083
--- /dev/null
+++ b/testunwind.c
@@ -0,0 +1,27 @@
+#include "elfparser.h"
+#include "unwind.h"
+
+int
+main (int argc, char **argv)
+{
+ const guchar *data;
+ ElfParser *elf;
+
+ if (argc == 1)
+ {
+ g_print ("no arg\n");
+ return -1;
+ }
+
+ elf = elf_parser_new (argv[1], NULL);
+
+ if (!elf)
+ {
+ g_print ("NO ELF!!!!\n");
+ return -1;
+ }
+
+ unwind (elf);
+
+ return 0;
+}
diff --git a/tracker.c b/tracker.c
new file mode 100644
index 0000000..9117052
--- /dev/null
+++ b/tracker.c
@@ -0,0 +1,1129 @@
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib/gprintf.h>
+
+#include "tracker.h"
+#include "stackstash.h"
+#include "binfile.h"
+#include "elfparser.h"
+#include "perf_counter.h"
+
+typedef struct new_process_t new_process_t;
+typedef struct new_map_t new_map_t;
+typedef struct sample_t sample_t;
+typedef struct fork_t fork_t;
+typedef struct exit_t exit_t;
+
+struct tracker_t
+{
+ StackStash *stash;
+
+ size_t n_event_bytes;
+ size_t n_allocated_bytes;
+ uint8_t * events;
+};
+
+typedef enum
+{
+ NEW_PROCESS,
+ NEW_MAP,
+ SAMPLE,
+ FORK,
+ EXIT
+} event_type_t;
+
+struct new_process_t
+{
+ uint32_t header;
+ char command_line[256];
+};
+
+struct fork_t
+{
+ uint32_t header;
+ int32_t child_pid;
+};
+
+struct exit_t
+{
+ uint32_t header;
+};
+
+struct new_map_t
+{
+ uint32_t header;
+ char filename[PATH_MAX];
+ uint64_t start;
+ uint64_t end;
+ uint64_t offset;
+ uint64_t inode;
+};
+
+struct sample_t
+{
+ uint32_t header;
+ StackNode * trace;
+};
+
+#define TYPE_SHIFT 29
+#define PID_MASK ((uint32_t)((1 << TYPE_SHIFT) - 1))
+
+#define MAKE_HEADER(type, pid) \
+ ((uint32_t)((((uint32_t)pid) & PID_MASK) | (type << TYPE_SHIFT)))
+
+#define GET_PID(header) \
+ (header & PID_MASK)
+
+#define GET_TYPE(header) \
+ (header >> TYPE_SHIFT)
+
+#define DEFAULT_SIZE (1024 * 1024 * 4)
+
+static char **
+get_lines (const char *format, pid_t pid)
+{
+ char *filename = g_strdup_printf (format, pid);
+ char **result = NULL;
+ char *contents;
+
+ if (g_file_get_contents (filename, &contents, NULL, NULL))
+ {
+ result = g_strsplit (contents, "\n", -1);
+
+ g_free (contents);
+ }
+
+ g_free (filename);
+
+ return result;
+}
+
+static void
+fake_new_process (tracker_t *tracker, pid_t pid)
+{
+ char **lines;
+ gboolean done = FALSE;
+
+ if ((lines = get_lines ("/proc/%d/cmdline", pid)))
+ {
+ if (lines[0] && strlen (lines[0]) > 0)
+ {
+ tracker_add_process (tracker, pid, lines[0]);
+
+ done = TRUE;
+ }
+
+ g_strfreev (lines);
+ }
+
+ if (!done && (lines = get_lines ("/proc/%d/status", pid)))
+ {
+ int i;
+
+ for (i = 0; lines[i] != NULL; ++i)
+ {
+ if (strncmp ("Name:", lines[i], 5) == 0)
+ {
+ char *name = g_strstrip (strchr (lines[i], ':') + 1);
+
+ if (strlen (name) > 0)
+ {
+ tracker_add_process (tracker, pid, name);
+ done = TRUE;
+ break;
+ }
+ }
+ }
+
+ g_strfreev (lines);
+ }
+
+ if (!done)
+ g_print ("failed to fake %d\n", pid);
+}
+
+static void
+fake_new_map (tracker_t *tracker, pid_t pid)
+{
+ char **lines;
+
+ if ((lines = get_lines ("/proc/%d/maps", pid)))
+ {
+ int i;
+
+ for (i = 0; lines[i] != NULL; ++i)
+ {
+ char file[256];
+ gulong start;
+ gulong end;
+ gulong offset;
+ gulong inode;
+ int count;
+
+ file[255] = '\0';
+
+ count = sscanf (
+ lines[i], "%lx-%lx %*15s %lx %*x:%*x %lu %255s",
+ &start, &end, &offset, &inode, file);
+
+ if (count == 5)
+ {
+ if (strcmp (file, "[vdso]") == 0)
+ {
+ /* For the vdso, the kernel reports 'offset' as the
+ * the same as the mapping addres. This doesn't make
+ * any sense to me, so we just zero it here. There
+ * is code in binfile.c (read_inode) that returns 0
+ * for [vdso].
+ */
+ offset = 0;
+ inode = 0;
+ }
+
+ tracker_add_map (tracker, pid, start, end, offset, inode, file);
+ }
+ }
+
+ g_strfreev (lines);
+ }
+}
+
+static void
+populate_from_proc (tracker_t *tracker)
+{
+ GDir *proc = g_dir_open ("/proc", 0, NULL);
+ const char *name;
+
+ if (!proc)
+ return;
+
+ while ((name = g_dir_read_name (proc)))
+ {
+ pid_t pid;
+ char *end;
+
+ pid = strtol (name, &end, 10);
+
+ if (*end == 0)
+ {
+ fake_new_process (tracker, pid);
+ fake_new_map (tracker, pid);
+ }
+ }
+
+ g_dir_close (proc);
+}
+
+
+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;
+}
+
+tracker_t *
+tracker_new (void)
+{
+ tracker_t *tracker = g_new0 (tracker_t, 1);
+ GTimeVal before, after;
+
+ tracker->n_event_bytes = 0;
+ tracker->n_allocated_bytes = DEFAULT_SIZE;
+ tracker->events = g_malloc (DEFAULT_SIZE);
+
+ tracker->stash = stack_stash_new (NULL);
+
+ g_get_current_time (&before);
+
+ populate_from_proc (tracker);
+
+ g_get_current_time (&after);
+
+ g_print ("Time to populate %f\n", time_diff (&after, &before));
+
+ return tracker;
+}
+
+void
+tracker_free (tracker_t *tracker)
+{
+ stack_stash_unref (tracker->stash);
+ g_free (tracker->events);
+ g_free (tracker);
+}
+
+#define COPY_STRING(dest, src) \
+ do \
+ { \
+ strncpy (dest, src, sizeof (dest) - 1); \
+ dest[sizeof (dest) - 1] = 0; \
+ } \
+ while (0)
+
+
+static void
+tracker_append (tracker_t *tracker,
+ void *event,
+ int n_bytes)
+{
+ if (tracker->n_allocated_bytes - tracker->n_event_bytes < n_bytes)
+ {
+ size_t new_size = tracker->n_allocated_bytes * 2;
+
+ tracker->events = g_realloc (tracker->events, new_size);
+
+ tracker->n_allocated_bytes = new_size;
+ }
+
+ g_assert (tracker->n_allocated_bytes - tracker->n_event_bytes >= n_bytes);
+
+ memcpy (tracker->events + tracker->n_event_bytes, event, n_bytes);
+
+#if 0
+ g_print (" (address %p)\n", tracker->events + tracker->n_event_bytes);
+#endif
+
+ tracker->n_event_bytes += n_bytes;
+}
+
+void
+tracker_add_process (tracker_t * tracker,
+ pid_t pid,
+ const char *command_line)
+{
+ new_process_t event;
+
+#if 0
+ g_print ("Add new process %s %d to tracker ", command_line, pid);
+#endif
+
+ event.header = MAKE_HEADER (NEW_PROCESS, pid);
+ COPY_STRING (event.command_line, command_line);
+
+ tracker_append (tracker, &event, sizeof (event));
+}
+
+void
+tracker_add_fork (tracker_t *tracker,
+ pid_t pid,
+ pid_t child_pid)
+{
+ fork_t event;
+
+ event.header = MAKE_HEADER(FORK, pid);
+ event.child_pid = child_pid;
+
+ tracker_append (tracker, &event, sizeof (event));
+}
+
+void
+tracker_add_exit (tracker_t *tracker,
+ pid_t pid)
+{
+ exit_t event;
+
+ event.header = MAKE_HEADER (EXIT, pid);
+
+ tracker_append (tracker, &event, sizeof (event));
+}
+
+void
+tracker_add_map (tracker_t * tracker,
+ pid_t pid,
+ uint64_t start,
+ uint64_t end,
+ uint64_t offset,
+ uint64_t inode,
+ const char *filename)
+{
+ new_map_t event;
+
+ event.header = MAKE_HEADER (NEW_MAP, pid);
+ COPY_STRING (event.filename, filename);
+ event.start = start;
+ event.end = end;
+ event.offset = offset;
+ event.inode = inode;
+
+ tracker_append (tracker, &event, sizeof (event));
+}
+
+void
+tracker_add_sample (tracker_t *tracker,
+ pid_t pid,
+ uint64_t *ips,
+ int n_ips)
+{
+ sample_t event;
+
+ event.header = MAKE_HEADER (SAMPLE, pid);
+ event.trace = stack_stash_add_trace (tracker->stash, ips, n_ips, 1);
+
+ tracker_append (tracker, &event, sizeof (event));
+}
+
+/* */
+typedef struct state_t state_t;
+typedef struct process_t process_t;
+typedef struct map_t map_t;
+
+struct process_t
+{
+ pid_t pid;
+
+ char * comm;
+
+ GPtrArray * maps;
+};
+
+struct map_t
+{
+ char * filename;
+ uint64_t start;
+ uint64_t end;
+ uint64_t offset;
+ uint64_t inode;
+};
+
+struct state_t
+{
+ GHashTable *processes_by_pid;
+ GHashTable *unique_comms;
+ GHashTable *unique_symbols;
+ GHashTable *bin_files;
+};
+
+static void
+destroy_map (map_t *map)
+{
+ g_free (map->filename);
+ g_free (map);
+}
+
+static void
+create_map (state_t *state, new_map_t *new_map)
+{
+ process_t *process;
+ map_t *map;
+ int i;
+ pid_t pid = GET_PID (new_map->header);
+
+ process = g_hash_table_lookup (
+ state->processes_by_pid, GINT_TO_POINTER (pid));
+
+ if (!process)
+ return;
+
+ map = g_new0 (map_t, 1);
+ map->filename = g_strdup (new_map->filename);
+ map->start = new_map->start;
+ map->end = new_map->end;
+ map->offset = new_map->offset;
+ map->inode = new_map->inode;
+
+ /* Remove existing maps that overlap the new one */
+ for (i = 0; i < process->maps->len; ++i)
+ {
+ map_t *m = process->maps->pdata[i];
+
+ if (m->start < map->end && m->end > map->start)
+ {
+ destroy_map (m);
+
+ g_ptr_array_remove_index (process->maps, i);
+ }
+ }
+
+ g_ptr_array_add (process->maps, map);
+}
+
+static void
+destroy_process (process_t *process)
+{
+ int i;
+
+ g_free (process->comm);
+
+ for (i = 0; i < process->maps->len; ++i)
+ {
+ map_t *map = process->maps->pdata[i];
+
+ destroy_map (map);
+ }
+
+ g_ptr_array_free (process->maps, TRUE);
+ g_free (process);
+}
+
+static void
+create_process (state_t *state, new_process_t *new_process)
+{
+ pid_t pid = GET_PID (new_process->header);
+ const char *comm = new_process->command_line;
+
+ process_t *process =
+ g_hash_table_lookup (state->processes_by_pid, GINT_TO_POINTER (pid));
+
+ if (process)
+ {
+ g_free (process->comm);
+ process->comm = g_strdup (comm);
+ }
+ else
+ {
+ process = g_new0 (process_t, 1);
+
+ process->pid = pid;
+ process->comm = g_strdup (comm);
+ process->maps = g_ptr_array_new ();
+
+ g_hash_table_insert (
+ state->processes_by_pid, GINT_TO_POINTER (process->pid), process);
+ }
+}
+
+static map_t *
+copy_map (map_t *map)
+{
+ map_t *copy = g_new0 (map_t, 1);
+
+ *copy = *map;
+ copy->filename = g_strdup (map->filename);
+
+ return copy;
+}
+
+static void
+process_fork (state_t *state, fork_t *fork)
+{
+ pid_t ppid = GET_PID (fork->header);
+
+ process_t *parent = g_hash_table_lookup (
+ state->processes_by_pid, GINT_TO_POINTER (ppid));
+ process_t *child;
+
+ if (ppid == fork->child_pid)
+ {
+ /* Just a new thread being spawned */
+ return;
+ }
+
+ child = g_new0 (process_t, 1);
+ if (parent)
+ child->comm = g_strdup (parent->comm);
+ else
+ child->comm = g_strdup_printf ("[pid %d]", fork->child_pid);
+
+ child->pid = fork->child_pid;
+
+ child->maps = g_ptr_array_new ();
+
+ if (parent)
+ {
+ int i;
+
+ for (i = 0; i < parent->maps->len; ++i)
+ {
+ map_t *map = copy_map (parent->maps->pdata[i]);
+
+ g_ptr_array_add (child->maps, map);
+ }
+ }
+
+ g_hash_table_insert (
+ state->processes_by_pid, GINT_TO_POINTER (child->pid), child);
+}
+
+static void
+process_exit (state_t *state, exit_t *exit)
+{
+#if 0
+ g_print ("Exit for %d\n", exit->pid);
+#endif
+
+ /* ignore for now */
+}
+
+static void
+free_process (gpointer data)
+{
+ process_t *process = data;
+
+ destroy_process (process);
+}
+
+static state_t *
+state_new (void)
+{
+ state_t *state = g_new0 (state_t, 1);
+
+ state->processes_by_pid =
+ g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, free_process);
+
+ state->unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal);
+ state->unique_comms = g_hash_table_new (g_str_hash, g_str_equal);
+ state->bin_files = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify)bin_file_free);
+
+ return state;
+}
+
+static void
+state_free (state_t *state)
+{
+ g_hash_table_destroy (state->processes_by_pid);
+ g_hash_table_destroy (state->unique_symbols);
+ g_hash_table_destroy (state->unique_comms);
+ g_hash_table_destroy (state->bin_files);
+
+ g_free (state);
+}
+
+typedef struct
+{
+ gulong address;
+ char *name;
+} kernel_symbol_t;
+
+static void
+parse_kallsym_line (const char *line, GArray *table)
+{
+ char **tokens = g_strsplit_set (line, " \t", -1);
+
+ if (tokens[0] && tokens[1] && tokens[2])
+ {
+ glong address;
+ char *endptr;
+
+ address = strtoul (tokens[0], &endptr, 16);
+
+ if (*endptr == '\0' &&
+ (strcmp (tokens[1], "T") == 0 ||
+ strcmp (tokens[1], "t") == 0))
+ {
+ kernel_symbol_t sym;
+
+ sym.address = address;
+ sym.name = g_strdup (tokens[2]);
+
+ g_array_append_val (table, sym);
+ }
+ }
+
+ g_strfreev (tokens);
+}
+
+static gboolean
+parse_kallsyms (const char *kallsyms,
+ GArray *table)
+{
+ const char *sol;
+ const char *eol;
+
+ sol = kallsyms;
+ eol = strchr (sol, '\n');
+ while (eol)
+ {
+ char *line = g_strndup (sol, eol - sol);
+
+ parse_kallsym_line (line, table);
+
+ g_free (line);
+
+ sol = eol + 1;
+ eol = strchr (sol, '\n');
+ }
+
+ if (table->len <= 1)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+compare_syms (gconstpointer a, gconstpointer b)
+{
+ const kernel_symbol_t *sym_a = a;
+ const kernel_symbol_t *sym_b = b;
+
+ if (sym_a->address > sym_b->address)
+ return 1;
+ else if (sym_a->address == sym_b->address)
+ return 0;
+ else
+ return -1;
+}
+
+static kernel_symbol_t *
+do_lookup (kernel_symbol_t *symbols,
+ gulong address,
+ int first,
+ int last)
+{
+ if (address >= symbols[last].address)
+ {
+ return &(symbols[last]);
+ }
+ else if (last - first < 3)
+ {
+ while (last >= first)
+ {
+ if (address >= symbols[last].address)
+ return &(symbols[last]);
+
+ last--;
+ }
+
+ return NULL;
+ }
+ else
+ {
+ int mid = (first + last) / 2;
+
+ if (symbols[mid].address > address)
+ return do_lookup (symbols, address, first, mid);
+ else
+ return do_lookup (symbols, address, mid, last);
+ }
+}
+
+static GArray *
+get_kernel_symbols (void)
+{
+ static GArray *kernel_syms;
+ static gboolean initialized = FALSE;
+
+#if 0
+ find_kernel_binary();
+#endif
+
+ if (!initialized)
+ {
+ char *kallsyms;
+ if (g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL))
+ {
+ if (kallsyms)
+ {
+ kernel_syms = g_array_new (TRUE, TRUE, sizeof (kernel_symbol_t));
+
+ if (parse_kallsyms (kallsyms, kernel_syms))
+ {
+ g_array_sort (kernel_syms, compare_syms);
+ }
+ else
+ {
+ g_array_free (kernel_syms, TRUE);
+ kernel_syms = NULL;
+ }
+ }
+
+ g_free (kallsyms);
+ }
+
+ if (!kernel_syms)
+ {
+ g_print ("Warning: /proc/kallsyms could not be "
+ "read. Kernel symbols will not be available\n");
+ }
+
+ initialized = TRUE;
+ }
+
+ return kernel_syms;
+}
+
+static const char skip_kernel_symbols[][32] =
+{
+ /* IRQ stack */
+ "common_interrupt",
+ "apic_timer_interrupt",
+ "smp_apic_timer_interrupt",
+ "hrtimer_interrupt",
+ "__run_hrtimer",
+ "perf_swevent_hrtimer",
+ "perf_event_overflow",
+ "__perf_event_overflow",
+ "perf_prepare_sample",
+ "perf_callchain",
+ "perf_swcounter_hrtimer",
+ "perf_counter_overflow",
+ "__perf_counter_overflow",
+ "perf_counter_output",
+
+ /* NMI stack */
+ "nmi_stack_correct",
+ "do_nmi",
+ "notify_die",
+ "atomic_notifier_call_chain",
+ "notifier_call_chain",
+ "perf_event_nmi_handler",
+ "perf_counter_nmi_handler",
+ "intel_pmu_handle_irq",
+ "perf_event_overflow",
+ "perf_counter_overflow",
+ "__perf_event_overflow",
+ "perf_prepare_sample",
+ "perf_callchain",
+
+ ""
+};
+
+const char *
+lookup_kernel_symbol (gulong address)
+{
+ kernel_symbol_t *result;
+ GArray *ksyms = get_kernel_symbols ();
+ const char *sym;
+ int i;
+
+ if (ksyms->len == 0)
+ return NULL;
+
+ result = do_lookup ((kernel_symbol_t *)ksyms->data,
+ address, 0, ksyms->len - 1);
+
+ sym = result? result->name : NULL;
+
+
+ /* This is a workaround for a kernel bug, where it reports not
+ * only the kernel stack, but also the IRQ stack for the
+ * timer interrupt that generated the stack.
+ *
+ * The stack as reported by the kernel looks like this:
+ *
+ * [ip] [irq stack] [real kernel stack]
+ *
+ * Below we filter out the [irq stack]
+ */
+ i = 0;
+ while (skip_kernel_symbols[i][0] != '\0')
+ {
+ if (strcmp (sym, skip_kernel_symbols[i]) == 0)
+ {
+ sym = NULL;
+ break;
+ }
+ i++;
+ }
+
+ return sym;
+}
+
+/* Note that 'unique_symbols' is a direct_hash table. Ie., we
+ * rely on the address of symbol strings being different for different
+ * symbols.
+ */
+static char *
+unique_dup (GHashTable *unique_symbols, const char *sym)
+{
+ char *result;
+
+ result = g_hash_table_lookup (unique_symbols, sym);
+ if (!result)
+ {
+ result = elf_demangle (sym);
+ g_hash_table_insert (unique_symbols, (char *)sym, result);
+ }
+
+ return result;
+}
+
+static map_t *
+process_locate_map (process_t *process, gulong addr)
+{
+ int i;
+
+ for (i = 0; i < process->maps->len; ++i)
+ {
+ map_t *map = process->maps->pdata[i];
+
+ if (addr >= map->start && addr < map->end)
+ return map;
+ }
+
+ return NULL;
+}
+
+static const char *
+make_message (state_t *state, const char *format, ...)
+{
+ va_list args;
+ char *message;
+ char *result;
+
+ va_start (args, format);
+ g_vasprintf (&message, format, args);
+ va_end (args);
+
+ result = g_hash_table_lookup (state->unique_comms, message);
+ if (result)
+ {
+ g_free (message);
+ }
+ else
+ {
+ result = message;
+
+ g_hash_table_insert (state->unique_comms, result, result);
+ }
+
+ return result;
+}
+
+static bin_file_t *
+state_get_bin_file (state_t *state, const char *filename)
+{
+ bin_file_t *bf = g_hash_table_lookup (state->bin_files, filename);
+
+ if (!bf)
+ {
+ bf = bin_file_new (filename);
+
+ g_hash_table_insert (state->bin_files, g_strdup (filename), bf);
+ }
+
+ return bf;
+}
+
+static char *
+lookup_symbol (state_t *state,
+ process_t *process,
+ uint64_t address,
+ gboolean kernel)
+{
+ const char *sym;
+
+ g_assert (process);
+
+ if (kernel)
+ {
+ sym = lookup_kernel_symbol (address);
+ }
+ else
+ {
+ map_t *map = process_locate_map (process, address);
+
+ if (!map)
+ {
+ sym = make_message (state, "No map [%s]", process->comm);
+ }
+ else
+ {
+ bin_file_t *bin_file = state_get_bin_file (state, map->filename);
+ const bin_symbol_t *bin_sym;
+
+ address -= map->start;
+ address += map->offset;
+
+ if (map->inode && !bin_file_check_inode (bin_file, map->inode))
+ {
+ /* If the inodes don't match, it's probably because the
+ * file has changed since the process was started.
+ */
+ sym = make_message (state, "%s: inode mismatch", map->filename);
+ }
+ else
+ {
+ bin_sym = bin_file_lookup_symbol (bin_file, address);
+
+ sym = bin_symbol_get_name (bin_file, bin_sym);
+ }
+ }
+ }
+
+ if (sym)
+ return unique_dup (state->unique_symbols, sym);
+ else
+ return NULL;
+}
+
+typedef struct context_info_t context_info_t;
+struct context_info_t
+{
+ enum perf_callchain_context context;
+ char name[32];
+};
+
+static const context_info_t context_info[] =
+{
+ { PERF_CONTEXT_HV, "- - hypervisor - -" },
+ { PERF_CONTEXT_KERNEL, "- - kernel - -" },
+ { PERF_CONTEXT_USER, "- - user - - " },
+ { PERF_CONTEXT_GUEST, "- - guest - - " },
+ { PERF_CONTEXT_GUEST_KERNEL, "- - guest kernel - -" },
+ { PERF_CONTEXT_GUEST_USER, "- - guest user - -" },
+};
+
+static const char *const everything = "[Everything]";
+
+static const context_info_t *
+get_context_info (enum perf_callchain_context context)
+{
+ int i;
+
+ for (i = 0; i < sizeof (context_info) / sizeof (context_info[0]); ++i)
+ {
+ const context_info_t *info = &context_info[i];
+
+ if (info->context == context)
+ return info;
+ }
+
+ return NULL;
+}
+
+static void
+process_sample (state_t *state, StackStash *resolved, sample_t *sample)
+{
+ const context_info_t *context = NULL;
+ const char *cmdline;
+ uint64_t stack_addrs[256];
+ uint64_t *resolved_traces;
+ process_t *process;
+ StackNode *n;
+ int len;
+ pid_t pid = GET_PID (sample->header);
+
+ process = g_hash_table_lookup (
+ state->processes_by_pid, GINT_TO_POINTER (pid));
+
+ if (!process)
+ {
+ static gboolean warned;
+ if (!warned || pid != 0)
+ {
+#if 0
+ g_print ("sample for unknown process %d\n", sample->pid);
+#endif
+ warned = TRUE;
+ }
+ return;
+ }
+
+ len = 5;
+ for (n = sample->trace; n != NULL; n = n->parent)
+ len++;
+
+ if (len > 256)
+ resolved_traces = g_new (uint64_t, len);
+ else
+ resolved_traces = stack_addrs;
+
+ len = 0;
+ for (n = sample->trace; n != NULL; n = n->parent)
+ {
+ uint64_t address = n->data;
+ const context_info_t *new_context;
+ const char *symbol;
+
+ new_context = get_context_info (address);
+ if (new_context)
+ {
+ if (context)
+ symbol = unique_dup (state->unique_symbols, context->name);
+ else
+ symbol = NULL;
+
+ context = new_context;
+ }
+ else
+ {
+ gboolean kernel = context && context->context == PERF_CONTEXT_KERNEL;
+
+ symbol = lookup_symbol (state, process, address, kernel);
+ }
+
+ if (symbol)
+ resolved_traces[len++] = POINTER_TO_U64 (symbol);
+ }
+
+ if (context && context->context != PERF_CONTEXT_USER)
+ {
+ /* Kernel threads do not have a user part, so we end up here
+ * without ever getting a user context. If this happens,
+ * add the '- - kernel - - ' name, so that kernel threads
+ * are properly blamed on the kernel
+ */
+ resolved_traces[len++] =
+ POINTER_TO_U64 (unique_dup (state->unique_symbols, context->name));
+ }
+
+ cmdline = make_message (state, "[%s]", process->comm);
+
+ resolved_traces[len++] = POINTER_TO_U64 (cmdline);
+ resolved_traces[len++] = POINTER_TO_U64 (
+ unique_dup (state->unique_symbols, everything));
+
+ stack_stash_add_trace (resolved, resolved_traces, len, 1);
+
+ if (resolved_traces != stack_addrs)
+ g_free (resolved_traces);
+}
+
+Profile *
+tracker_create_profile (tracker_t *tracker)
+{
+ uint8_t *end = tracker->events + tracker->n_event_bytes;
+ StackStash *resolved_stash;
+ Profile *profile;
+ state_t *state;
+ uint8_t *event;
+
+ state = state_new ();
+ resolved_stash = stack_stash_new (g_free);
+
+ event = tracker->events;
+ while (event < end)
+ {
+ event_type_t type = GET_TYPE (*(uint32_t *)event);
+
+ switch (type)
+ {
+ case NEW_PROCESS:
+ create_process (state, (new_process_t *)event);
+ event += sizeof (new_process_t);
+ break;
+
+ case NEW_MAP:
+ create_map (state, (new_map_t *)event);
+ event += sizeof (new_map_t);
+ break;
+
+ case FORK:
+ process_fork (state, (fork_t *)event);
+ event += sizeof (fork_t);
+ break;
+
+ case EXIT:
+ process_exit (state, (exit_t *)event);
+ event += sizeof (exit_t);
+ break;
+
+ case SAMPLE:
+ process_sample (state, resolved_stash, (sample_t *)event);
+ event += sizeof (sample_t);
+ break;
+ }
+ }
+
+ profile = profile_new (resolved_stash);
+
+ state_free (state);
+ stack_stash_unref (resolved_stash);
+
+ return profile;
+}
diff --git a/tracker.h b/tracker.h
new file mode 100644
index 0000000..098f4a1
--- /dev/null
+++ b/tracker.h
@@ -0,0 +1,29 @@
+#include <stdint.h>
+#include "profile.h"
+
+typedef struct tracker_t tracker_t;
+
+tracker_t *tracker_new (void);
+void tracker_free (tracker_t *);
+
+void tracker_add_process (tracker_t *tracker,
+ pid_t pid,
+ const char *command_line);
+void tracker_add_fork (tracker_t *tracker,
+ pid_t pid,
+ pid_t child_pid);
+void tracker_add_exit (tracker_t *tracker,
+ pid_t pid);
+void tracker_add_map (tracker_t * tracker,
+ pid_t pid,
+ uint64_t start,
+ uint64_t end,
+ uint64_t offset,
+ uint64_t inode,
+ const char *filename);
+void tracker_add_sample (tracker_t *tracker,
+ pid_t pid,
+ uint64_t *ips,
+ int n_ips);
+
+Profile *tracker_create_profile (tracker_t *tracker);
diff --git a/treeviewutils.c b/treeviewutils.c
index 2472eb6..20d7777 100644
--- a/treeviewutils.c
+++ b/treeviewutils.c
@@ -1,11 +1,9 @@
-/* -*- mode: C; c-file-style: "linux" -*- */
-
/* MemProf -- memory profiler and leak detector
* Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk)
* Copyright 2003, 2004, Red Hat, Inc.
*
* Sysprof -- Sampling, systemwide CPU profiler
- * Copyright 2004, 2005, Soeren Sandmann
+ * Copyright 2004, 2005, 2006, 2007, 2008 Soeren Sandmann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,74 +23,134 @@
#include "treeviewutils.h"
-void
-column_set_sort_id (GtkTreeViewColumn *column,
- int column_id)
+static void on_column_clicked (GtkTreeViewColumn *column,
+ gpointer data);
+typedef struct
{
- g_object_set_data (G_OBJECT (column),
- "mi-saved-sort-column", GINT_TO_POINTER (column_id));
- gtk_tree_view_column_set_sort_column_id (column, column_id);
-}
+ int model_column;
+ GtkSortType default_order;
+} SortInfo;
-void
-tree_view_unset_sort_ids (GtkTreeView *tree_view)
+static void
+set_sort_info (GtkTreeView *view,
+ GtkTreeViewColumn *column,
+ int model_column,
+ GtkSortType default_order)
{
- GList *columns = gtk_tree_view_get_columns (tree_view);
- GList *l;
-
- for (l = columns; l; l = l->next) {
- gtk_tree_view_column_set_sort_column_id (l->data, -1);
- }
-
- g_list_free (columns);
+ SortInfo *info;
+
+ info = g_new0 (SortInfo, 1);
+ info->model_column = model_column;
+ info->default_order = default_order;
+
+ gtk_tree_view_column_set_clickable (column, TRUE);
+
+ g_object_set_data (G_OBJECT (column), "column_info", info);
+ g_signal_connect (column, "clicked", G_CALLBACK (on_column_clicked), view);
}
-void
-tree_view_set_sort_ids (GtkTreeView *tree_view)
+static SortInfo *
+get_sort_info (GtkTreeViewColumn *column)
{
- GList *columns = gtk_tree_view_get_columns (tree_view);
- GList *l;
-
- for (l = columns; l; l = l->next) {
- int column_id = GPOINTER_TO_INT (g_object_get_data (l->data, "mi-saved-sort-column"));
- gtk_tree_view_column_set_sort_column_id (l->data, column_id);
- }
-
- g_list_free (columns);
+ return g_object_get_data (G_OBJECT (column), "column_info");
}
-int
-list_iter_get_index (GtkTreeModel *model,
- GtkTreeIter *iter)
+static GtkTreeViewColumn *
+find_column_by_model_column (GtkTreeView *view,
+ int model_column)
{
- GtkTreePath *path = gtk_tree_model_get_path (model,iter);
- int result;
+ GList *columns = gtk_tree_view_get_columns (view);
+ GList *list;
+ GtkTreeViewColumn *result = NULL;
+
+ for (list = columns; list != NULL; list = list->next)
+ {
+ GtkTreeViewColumn *column = list->data;
+ SortInfo *info = get_sort_info (column);
- g_assert (path);
- g_assert (gtk_tree_path_get_depth (path) == 1);
- result = gtk_tree_path_get_indices (path)[0];
- gtk_tree_path_free (path);
+ if (info->model_column == model_column)
+ result = column;
+ }
+
+ g_list_free (columns);
+
+ return result;
+}
- return result;
+void
+tree_view_set_sort_column (GtkTreeView *view,
+ int model_column,
+ int sort_type)
+{
+ GList *columns, *list;
+ GtkTreeSortable *sortable;
+ GtkTreeViewColumn *column = find_column_by_model_column (view, model_column);
+ SortInfo *info = get_sort_info (column);
+
+ /* For each column in the view, unset the sort indicator */
+ columns = gtk_tree_view_get_columns (view);
+ for (list = columns; list != NULL; list = list->next)
+ {
+ GtkTreeViewColumn *col = GTK_TREE_VIEW_COLUMN (list->data);
+
+ gtk_tree_view_column_set_sort_indicator (col, FALSE);
+ }
+
+ /* Set the sort indicator for this column */
+ gtk_tree_view_column_set_sort_indicator (column, TRUE);
+ gtk_tree_view_column_set_sort_order (column, sort_type);
+
+ /* And sort the column */
+ sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (view));
+
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ info->model_column,
+ sort_type);
+}
+static void
+on_column_clicked (GtkTreeViewColumn *column,
+ gpointer data)
+{
+ GtkTreeView *view = data;
+ GtkSortType sort_type;
+ SortInfo *info = get_sort_info (column);
+
+ /* Find out what our current sort indicator is. If it is NONE, then
+ * select the default for the column, otherwise, select the opposite
+ */
+ if (!gtk_tree_view_column_get_sort_indicator (column))
+ {
+ sort_type = info->default_order;
+ }
+ else
+ {
+ if (gtk_tree_view_column_get_sort_order (column) == GTK_SORT_ASCENDING)
+ sort_type = GTK_SORT_DESCENDING;
+ else
+ sort_type = GTK_SORT_ASCENDING;
+ }
+
+ tree_view_set_sort_column (view, info->model_column, sort_type);
}
GtkTreeViewColumn *
add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column)
{
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
-
- renderer = gtk_cell_renderer_text_new ();
- g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
- column = gtk_tree_view_column_new_with_attributes (title, renderer,
- "text", model_column,
- NULL);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_append_column (view, column);
- column_set_sort_id (column, model_column);
-
- return column;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ column = gtk_tree_view_column_new_with_attributes (title, renderer,
+ "text", model_column,
+ NULL);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_append_column (view, column);
+
+ set_sort_info (view, column, model_column, GTK_SORT_ASCENDING);
+
+ return column;
}
static void
@@ -100,19 +158,19 @@ pointer_to_text (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell, GtkTreeModel *tree_model,
GtkTreeIter *iter, gpointer data)
{
- gpointer p;
- gchar *text;
- int column = GPOINTER_TO_INT (data);
-
- gtk_tree_model_get (tree_model, iter, column, &p, -1);
- text = g_strdup_printf ("%p", p);
- g_object_set (cell, "text", text, NULL);
- g_free (text);
+ gpointer p;
+ gchar *text;
+ int column = GPOINTER_TO_INT (data);
+
+ gtk_tree_model_get (tree_model, iter, column, &p, -1);
+ text = g_strdup_printf ("%p", p);
+ g_object_set (cell, "text", text, NULL);
+ g_free (text);
}
typedef struct {
- int column;
- char *format;
+ int column;
+ char *format;
} ColumnInfo;
static void
@@ -120,113 +178,185 @@ double_to_text (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell, GtkTreeModel *tree_model,
GtkTreeIter *iter, gpointer data)
{
- gdouble d;
- gchar *text;
- ColumnInfo *info = data;
-
- gtk_tree_model_get (tree_model, iter, info->column, &d, -1);
-
- text = g_strdup_printf (info->format, d);
-
- g_object_set (cell, "text", text, NULL);
- g_free (text);
+ gdouble d;
+ gchar *text;
+ ColumnInfo *info = data;
+
+ gtk_tree_model_get (tree_model, iter, info->column, &d, -1);
+
+ text = g_strdup_printf (info->format, d);
+
+ g_object_set (cell, "markup", text, NULL);
+ g_free (text);
}
static void
free_column_info (void *data)
{
- ColumnInfo *info = data;
- g_free (info->format);
- g_free (info);
+ ColumnInfo *info = data;
+ g_free (info->format);
+ g_free (info);
}
GtkTreeViewColumn *
-add_double_format_column (GtkTreeView *view, const gchar *title, gint model_column, const char *format)
+add_double_format_column (GtkTreeView *view,
+ const gchar *title,
+ gint model_column,
+ const char *format)
{
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
- ColumnInfo *column_info = g_new (ColumnInfo, 1);
-
- renderer = gtk_cell_renderer_text_new ();
- g_object_set (renderer, "xalign", 1.0, NULL);
-
- column = gtk_tree_view_column_new ();
- gtk_tree_view_column_set_title (column, title);
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
- gtk_tree_view_column_set_resizable (column, FALSE);
-
- column_info->column = model_column;
- column_info->format = g_strdup (format);
-
- gtk_tree_view_column_set_cell_data_func (column, renderer,
- double_to_text, column_info, free_column_info);
-
- gtk_tree_view_append_column (view, column);
- column_set_sort_id (column, model_column);
-
- return column;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ ColumnInfo *column_info = g_new (ColumnInfo, 1);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "xalign", 1.0, NULL);
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, title);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_resizable (column, FALSE);
+
+ column_info->column = model_column;
+ column_info->format = g_strdup (format);
+
+ gtk_tree_view_column_set_cell_data_func (
+ column, renderer, double_to_text, column_info, free_column_info);
+
+ gtk_tree_view_append_column (view, column);
+
+ set_sort_info (view, column, model_column, GTK_SORT_DESCENDING);
+
+ return column;
}
GtkTreeViewColumn *
add_pointer_column (GtkTreeView *view, const gchar *title, gint model_column)
{
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
-
- renderer = gtk_cell_renderer_text_new ();
-
- column = gtk_tree_view_column_new ();
- if (title)
- gtk_tree_view_column_set_title (column, title);
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_cell_data_func (column, renderer,
- pointer_to_text, GINT_TO_POINTER (model_column), NULL);
-
- gtk_tree_view_append_column (view, column);
- column_set_sort_id (column, model_column);
-
- return column;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ column = gtk_tree_view_column_new ();
+ if (title)
+ gtk_tree_view_column_set_title (column, title);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_cell_data_func (
+ column, renderer, pointer_to_text, GINT_TO_POINTER (model_column), NULL);
+
+ gtk_tree_view_append_column (view, column);
+
+ return column;
}
-typedef struct
+void
+tree_view_set_model_with_default_sort (GtkTreeView *view,
+ GtkTreeModel *model,
+ int model_column,
+ GtkSortType default_sort)
{
- gboolean is_sorted;
- int sort_column;
- GtkSortType sort_type;
-} SortState;
-
+ int column;
+ GtkSortType type;
+ GtkTreeSortable *old_model;
+ GtkAdjustment *adjustment;
+
+ old_model = GTK_TREE_SORTABLE (gtk_tree_view_get_model (view));
+
+ if (!(old_model && gtk_tree_sortable_get_sort_column_id (
+ GTK_TREE_SORTABLE (old_model), &column, &type)))
+ {
+ column = model_column;
+ type = default_sort;
+ }
+
+ /* Setting the sort column here prevents the "rows_reordered"
+ * signal from being emitted when the model is attached to
+ * a treeview. This is desirable because at that point there
+ * is a handler attached, which means the signal will actually
+ * be emitted.
+ */
+ gtk_tree_sortable_set_sort_column_id (
+ GTK_TREE_SORTABLE (model), column, type);
+
+ gtk_tree_view_set_model (view, model);
+
+ tree_view_set_sort_column (view, column, type);
+
+ /* Workaround for GTK+ crack, see bug 405625 */
+ adjustment = gtk_tree_view_get_vadjustment (view);
+ if (adjustment)
+ gtk_adjustment_set_value (adjustment, 0);
+}
-gpointer
-save_sort_state (GtkTreeView *view)
+static void
+process_iter (GtkTreeView *view,
+ GtkTreeIter *iter,
+ VisibleCallback callback,
+ gpointer data)
{
- SortState *state = NULL;
- GtkTreeModel *model = gtk_tree_view_get_model (view);
-
- if (model && GTK_IS_TREE_SORTABLE (model)) {
- state = g_new (SortState, 1);
- state->is_sorted = gtk_tree_sortable_get_sort_column_id (
- GTK_TREE_SORTABLE (model),
- &(state->sort_column),
- &(state->sort_type));
+ GtkTreeModel *model = gtk_tree_view_get_model (view);
+ GtkTreePath *path;
+ GtkTreeIter child;
+
+ path = gtk_tree_model_get_path (model, iter);
+
+ callback (view, path, iter, data);
+
+ if (gtk_tree_view_row_expanded (view, path))
+ {
+ if (gtk_tree_model_iter_children (model, &child, iter))
+ {
+ do
+ {
+ process_iter (view, &child, callback, data);
+ }
+ while (gtk_tree_model_iter_next (model, &child));
}
- return state;
+ }
+
+ gtk_tree_path_free (path);
}
void
-restore_sort_state (GtkTreeView *view, gpointer st)
-{
- SortState *state = st;
- GtkTreeModel *model = gtk_tree_view_get_model (view);
-
- if (state) {
- if (state->is_sorted && model && GTK_IS_TREE_SORTABLE (model)) {
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
- state->sort_column,
- state->sort_type);
- }
- g_free (state);
- }
-
- gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (model));
+tree_view_foreach_visible (GtkTreeView *view,
+ VisibleCallback callback,
+ gpointer data)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (view);
+ GtkTreeIter iter;
+
+ if (model && gtk_tree_model_get_iter_first (model, &iter))
+ process_iter (view, &iter, callback, data);
}
+
+/* Not really a *treeview* utility, but there isn't really a
+ * better place to put it
+ */
+void
+set_error (GError **err, gint domain, gint code,
+ const char *format, va_list args)
+{
+ char *msg;
+
+ if (!err)
+ return;
+
+ msg = g_strdup_vprintf (format, args);
+
+ if (*err == NULL)
+ {
+ *err = g_error_new_literal (G_MARKUP_ERROR, code, msg);
+ }
+ else
+ {
+ /* Warning text from GLib */
+ g_warning ("GError set over the top of a previous GError or uninitialized memory.\n"
+ "This indicates a bug in someone's code. You must ensure an error is NULL before it's set.\n"
+ "The overwriting error message was: %s",
+ msg);
+ }
+
+ g_free (msg);
+}
+
diff --git a/treeviewutils.h b/treeviewutils.h
index 908a22c..a6eb1e5 100644
--- a/treeviewutils.h
+++ b/treeviewutils.h
@@ -22,14 +22,6 @@
#include <gtk/gtk.h>
-void column_set_sort_id (GtkTreeViewColumn *column,
- int column_id);
-void tree_view_unset_sort_ids (GtkTreeView *tree_view);
-void tree_view_set_sort_ids (GtkTreeView *tree_view);
-
-int list_iter_get_index (GtkTreeModel *model,
- GtkTreeIter *iter);
-
GtkTreeViewColumn *add_plain_text_column (GtkTreeView *view,
const char *title,
gint model_column);
@@ -40,6 +32,24 @@ GtkTreeViewColumn *add_double_format_column (GtkTreeView *view,
GtkTreeViewColumn *add_pointer_column (GtkTreeView *view,
const char *title,
int model_column);
-gpointer save_sort_state (GtkTreeView *view);
-void restore_sort_state (GtkTreeView *view,
- gpointer state);
+void tree_view_set_model_with_default_sort (GtkTreeView *view,
+ GtkTreeModel *model,
+ int model_column,
+ GtkSortType default_sort);
+void tree_view_set_sort_column (GtkTreeView *view,
+ int model_column,
+ int sort_type);
+
+typedef void (* VisibleCallback) (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+void tree_view_foreach_visible (GtkTreeView *view,
+ VisibleCallback callback,
+ gpointer data);
+
+
+
+void
+set_error_va (GError **err, gint domain, gint code,
+ const char *format, va_list args);
diff --git a/unwind.c b/unwind.c
new file mode 100644
index 0000000..d7c23e1
--- /dev/null
+++ b/unwind.c
@@ -0,0 +1,399 @@
+#include "elfparser.h"
+#include <string.h>
+#include "util.h"
+
+/* Pointer encodings, from dwarf2.h. */
+typedef enum
+{
+ DW_EH_PE_absptr = 0x00, /* */
+ DW_EH_PE_omit = 0xff, /* Value is not there */
+
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0A,
+ DW_EH_PE_sdata4 = 0x0B,
+ DW_EH_PE_sdata8 = 0x0C,
+ DW_EH_PE_signed = 0x08,
+
+ DW_EH_PE_pcrel = 0x10, /* Value is *(cur + val) */
+ DW_EH_PE_textrel = 0x20, /* Value is *(&text + val) */
+ DW_EH_PE_datarel = 0x30, /* Value is *(&data + val) */
+ DW_EH_PE_funcrel = 0x40, /* Value is *(fde.pc_begin + val) */
+ DW_EH_PE_aligned = 0x50, /* Value is absolute, and stored
+ * at next align */
+
+ DW_EH_PE_indirect = 0x80
+} PointerEncoding;
+
+typedef struct EncodedPointer EncodedPointer;
+struct EncodedPointer
+{
+ PointerEncoding encoding;
+ guint64 value;
+};
+
+static guint64
+get_length (const guchar **data)
+{
+ guint64 len;
+
+ len = *(guint32 *)*data;
+
+ *data += 4;
+
+ if (len == 0xffffffff)
+ {
+ len = *(guint64 *)data;
+ *data += 8;
+ }
+
+ return len;
+}
+
+static guint64
+decode_uleb128 (const guchar **data)
+{
+ guint64 result;
+ int shift;
+ guchar b;
+
+ result = 0;
+ shift = 0;
+ do
+ {
+ b = *(*data)++;
+ result |= (b & 0x7f) << shift;
+ shift += 7;
+
+ } while (b & 0x80);
+
+ return result;
+}
+
+static gint64
+decode_sleb128 (const guchar **data)
+{
+ gint64 result;
+ int shift;
+ guchar b;
+
+ result = 0;
+ shift = 0;
+ do
+ {
+ b = *(*data)++;
+ result |= (b & 0x7f) << shift;
+ shift += 7;
+ } while (b & 0x80);
+
+ if (b & 0x40 && shift < 64)
+ result |= - (1 << shift);
+
+ return result;
+}
+
+static void
+decode_block (const guchar **data)
+{
+ int len;
+
+ /* FIXME */
+
+ len = decode_uleb128 (data);
+
+ (*data) += len;
+}
+
+static gulong
+decode_address (const guchar **data)
+{
+ /* FIXME */
+ gulong r;
+
+ r = *(guint32 *)*data;
+ (*data) += 4;
+ return r;
+}
+
+static const char *
+decode_instruction (const guchar **data)
+{
+ int opcode = *(*data)++;
+ int high2 = (opcode & 0xc0) >> 6;
+ int low6 = (opcode & 0x3f);
+
+ if (high2 == 0x01)
+ {
+ return "DW_CFA_advance_loc";
+ }
+ else if (high2 == 0x02)
+ {
+ g_print ("register: %d\n", low6);
+ g_print ("offset: "FMT64"\n", decode_uleb128 (data));
+
+ return "DW_CFA_offset";
+ }
+ else if (high2 == 0x03)
+ {
+ return "DW_CFA_restore";
+ }
+ else
+ {
+ g_assert ((opcode & 0xc0) == 0);
+
+ switch (opcode)
+ {
+ case 0x0:
+ return "DW_CFA_nop";
+
+ case 0x01:
+ g_print ("addr: %p\n", (void *)decode_address (data));
+ return "DW_CFA_set_loc";
+
+ case 0x02:
+ (*data)++;
+ return "DW_CFA_advance_loc1";
+
+ case 0x03:
+ (*data) += 2;
+ return "DW_CFA_advance_loc2";
+
+ case 0x04:
+ (*data) += 4;
+ return "DW_CFA_advance_loc4";
+
+ case 0x05:
+ decode_uleb128 (data);
+ decode_uleb128 (data);
+ return "DW_CFA_offset_extended";
+
+ case 0x06:
+ decode_uleb128 (data);
+ return "DW_CFA_restore_extended";
+
+ case 0x07:
+ decode_uleb128 (data);
+ return "DW_CFA_undefined";
+
+ case 0x08:
+ decode_uleb128 (data);
+ return "DW_CFA_same_value";
+
+ case 0x09:
+ decode_uleb128 (data);
+ decode_uleb128 (data);
+ return "DW_CFA_register";
+
+ case 0x0a:
+ return "DW_CFA_remember_state";
+
+ case 0x0b:
+ return "DW_CFA_restore_state";
+
+ case 0x0c:
+ g_print ("reg: "FMT64"\n", decode_uleb128 (data));
+ g_print ("off: "FMT64"\n", decode_uleb128 (data));
+ return "DW_CFA_def_cfa";
+
+ case 0x0d:
+ decode_uleb128 (data);
+ return "DW_CFA_def_cfa_register";
+
+ case 0x0e:
+ decode_uleb128 (data);
+ return "DW_CFA_def_cfa_offset";
+
+ case 0x0f:
+ decode_block (data);
+ return "DW_CFA_def_cfa_expression";
+
+ case 0x10:
+ decode_uleb128 (data);
+ decode_block (data);
+ return "DW_CFA_expression";
+
+ case 0x11:
+ decode_uleb128 (data);
+ decode_sleb128 (data);
+ return "DW_CFA_offset_extended_sf";
+
+ case 0x12:
+ decode_uleb128 (data);
+ decode_sleb128 (data);
+ return "DW_CFA_def_cfa_sf";
+
+ case 0x13:
+ decode_sleb128 (data);
+ return "DW_CFA_def_cfa_offset_sf";
+
+ case 0x14:
+ decode_uleb128 (data);
+ decode_uleb128 (data);
+ return "DW_CFA_val_offset";
+
+ case 0x15:
+ decode_uleb128 (data);
+ decode_sleb128 (data);
+ return "DW_CFA_val_offset_sf";
+
+ case 0x16:
+ decode_uleb128 (data);
+ decode_block (data);
+ return "DW_CFA_val_expression";
+
+ case 0x1c:
+ return "DW_CFA_lo_user";
+
+ case 0x3f:
+ return "DW_CFA_hi_user";
+
+ default:
+ return "UNKNOWN INSTRUCTION";
+ }
+ }
+}
+
+typedef struct CIE CIE;
+struct CIE
+{
+ PointerEncoding encoding;
+};
+
+static void
+decode_cie (const guchar **data, const guchar *end)
+{
+ gboolean has_augmentation;
+ guint64 aug_len;
+ const char *augmentation;
+ CIE *cie;
+ int i, field;
+
+ g_print ("version: %d\n", *(*data)++);
+
+ augmentation = (*data);
+
+ *data += strlen (*data) + 1;
+
+ g_print ("code alignment: "FMT64"\n", decode_uleb128 (data));
+
+ g_print ("data alignment: %lld\n", decode_sleb128 (data));
+
+ g_print ("return register: "FMT64"\n", decode_uleb128 (data));
+
+ g_print ("augmentation: %s\n", augmentation);
+
+ if (augmentation[0] == 'z')
+ {
+ aug_len = decode_uleb128 (data);
+
+ g_print ("len: "FMT64"\n", aug_len);
+
+ for (i = 1; augmentation[i] != 0; ++i)
+ {
+ if (augmentation[i] == 'L')
+ {
+
+ }
+ }
+ }
+
+
+ if (has_augmentation)
+ {
+ g_print ("%x\n", **data);
+
+ *data += aug_len;
+ }
+
+ while (*data < end)
+ g_print (" %s\n", decode_instruction (data));
+}
+
+static gboolean
+decode_fde (const guchar **data, const guchar *end)
+{
+
+
+ return FALSE;
+}
+
+static gboolean
+decode_entry (const guchar **data, gboolean eh_frame)
+{
+ guint64 len;
+ const guchar *end;
+ gboolean is_cie;
+ guint64 id;
+
+ len = get_length (data);
+
+ if (len == 0)
+ return FALSE;
+
+ end = *data + len;
+
+ g_print ("length: "FMT64"\n", len);
+
+ /* CIE_id is 0 for eh frames, and 0xffffffff/0xffffffffffffffff for .debug_frame */
+
+ id = *(guint32 *)*data;
+
+ g_print ("id: %lld\n", id);
+
+ is_cie = (eh_frame && id == 0) || (!eh_frame && id == 0xffffffff);
+
+ if (is_cie)
+ g_print ("is cie\n");
+ else
+ g_print ("is not cie\n");
+
+ *data += 4;
+
+ if (is_cie)
+ decode_cie (data, end);
+ else
+ decode_fde (data, end);
+
+ return TRUE;
+}
+
+/* The correct API is probably something like
+ *
+ * gboolean
+ * unwind (ElfParser *parser,
+ * gulong *regs
+ * int n_regs,
+ * MemoryReader reader,
+ * gpointer data);
+ *
+ */
+void
+unwind (ElfParser *elf)
+{
+ const guchar *data;
+ gboolean eh_f;
+
+ if ((data = elf_parser_get_debug_frame (elf)))
+ {
+ g_print ("Using .debug_frame\n");
+ eh_f = FALSE;
+ }
+ else if ((data = elf_parser_get_eh_frame (elf)))
+ {
+ g_print ("Using .eh_frame\n");
+ eh_f = TRUE;
+ }
+ else
+ {
+ g_print ("no debug info found\n");
+ return;
+ }
+
+ while (decode_entry (&data, eh_f))
+ return ;
+ ;
+}
+
diff --git a/unwind.h b/unwind.h
new file mode 100644
index 0000000..2827e74
--- /dev/null
+++ b/unwind.h
@@ -0,0 +1,3 @@
+#include <glib.h>
+
+void unwind (const guchar *data);
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..c1fb1df
--- /dev/null
+++ b/util.h
@@ -0,0 +1,40 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#define FMT64 "%"G_GUINT64_FORMAT
+
+#if defined(__i386__)
+#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#endif
+
+#if defined(__x86_64__)
+#define rmb() asm volatile("lfence" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#endif
+
+#ifdef __powerpc__
+#define rmb() asm volatile ("sync" ::: "memory")
+#define cpu_relax() asm volatile ("" ::: "memory");
+#endif
+
+#ifdef __s390__
+#define rmb() asm volatile("bcr 15,0" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#endif
+
+#ifdef __sh__
+#if defined(__SH4A__) || defined(__SH5__)
+# define rmb() asm volatile("synco" ::: "memory")
+#else
+# define rmb() asm volatile("" ::: "memory")
+#endif
+#define cpu_relax() asm volatile("" ::: "memory")
+#endif
+
+#ifdef __hppa__
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#endif
+
+#endif