chroniquery

Get the code

chroniquery itself:

bzr branch http://www.visophyte.org/rev_control/bzr/chroniquery/trunk/ chroniquery

chronicle-recorder:

bzr branch http://www.visophyte.org/rev_control/bzr/chronicle-recorder/visbrero-integration/ chronicle-recorder
cd chronicle-recorder/chronicle
mv valgrind old-valgrind
bzr branch http://www.visophyte.org/rev_control/bzr/valgrind-chronicle/valgrind-chronicle/ valgrind

Build chronicle

in chronicle-recorder, make

Set up your scripts/paths

You want the chronicle-recorder/chronicle/chronicle-query binary to be on your path; symlinking is fine.

You may want to put chroniquery/python/chronisole.py on your path too, so that you can invoke it from anywhere.

You probably also want to create a “chronicle” script that allows you to easily run a program under chronicle.  For example, you might use the following as a basis:

#!/bin/sh
CHRONBASE=/path/to/chronicle-recorder/chronicle/
appname=$1
shift
PATH=$CHRONBASE:$PATH CHRONICLE_DB=`basename $appname`.db VALGRIND_LIB=$CHRONBASE/valgrind/.in_place $CHRONBASE/valgrind/coregrind/valgrind --tool=chronicle $appname "$@"

(You can also find the script in chroniquery/scripts, but you’ll still need to modify it.)

Run something under chronicle

Let’s try an example program from chroniquery/examples:

g++ fancy.cc -g -o fancy
chronicle ./fancy

This should result in chronicle leaving a ‘fancy.db’ file in the directory.

Now, let’s use the “trace” mechanism:

chronisole.py trace fancy -f main

This tells chronisole to run the “trace” command against the “fancy” program.  fancy.db is implied, but you could also have done chronisole.py trace fancy fancy.db -f main.  The “-f main” indicates that you want to start tracing from the ‘main’ function.

Doing fancy javascript aware tracing

First, it’s a good idea to tell chronisole how to ignore the stuff we don’t care about.  For now, this is done via the ~/.chroniquery.cfg file.  Here are the contents of mine:

[default]
interesting=false
recurse=false

[templates]
boring=true

[dir@mail]
interesting=true
depth=2

[dir@mailnews]
interesting=true
depth=3

[dir@mozilla/db/mork]
interesting=true

# Blanket boring mozilla
[dir@mozilla]
boring=true

# Now opt-in things that are perhaps not actually boring...
[dir@mozilla/modules/libpref]
boring=false
depth=0

# Inline things are jerks and need to be banned until our logic improves
#  (can it? I'm not sure DWARF is on our side here...)
[func@getter_Copies]
boring=true
[class@nsCGetterCopies]
boring=true

[class@nsID]
boring=true
[class@nsIJSID]
boring=true
[class@nsAutoRefCnt]
boring=true

[func@Substring]
boring=true
[class@nsCString]
boring=true
[class@nsString]
boring=true
[class@nsCAutoString]
boring=true
[class@nsAutoString]
boring=True
[class@nsDependentCString]
boring=true
[class@nsDependentString]
boring=true
[class@nsDependentSubstring]
boring=true
[class@nsFixedCString]
boring=true
[class@nsFixedString]
boring=true
[class@nsACstring]
boring=true
[class@nsAString]
boring=true
[class@nsACString_internal]
boring=true
[class@nsAString_internal]
boring=true
[class@nsSubstring_base]
boring=true
[class@nsXPIDLCString]
boring=true

[class@NS_ConvertUTF8toUTF16]
boring=true

Without this, chronicle would want to trace into everything.

BEWARE OF HACKS

So, you may note in the above configuration file, we have some paths.  If you grep for “evil” in the code, you may also note that there is a Chronifer._evilNormalizePath method in chronifer.py.  That evil method assumes that you are using comm-central and the top of the repository is also named that way; if this turns out to be true, it strips the path up through comm-central/.  It now also falls back to the possibility that your repository is named mozilla-central, stripping up through mozilla-central/ and replacing it with just “mozilla/”.  It assumes that if you have objdirs for your builds, that the objdir starts with “obj-” and that it is found under comm-central or mozilla-central.

This should clearly either not be hardcoded or not stupid.

END BEWARE OF HACKS

Let’s assume you want to trace an xpcshell run.  First, you probably want something like this patch against comm-central chroni-test and this wrapper script chronicle-xpcshell (rather like the above helper script, but targetting /tmp) that you would place in dist/bin.  You also need debug symbols, so your .mozconfig should have –enable-debugger-info-modules=yes in it.  You don’t have to –enable-debug or –disable-optimize, in fact you may not want to (as the DEBUG hit and lack of optimization will get even worse with chronicle), but you may want to ramp down the optimization using –enable-optimize=-O1 if you are leaving optimization on.  Then you can do:

USECHRONICLE=1 SOLO_FILE=”test_name.js” check-one

So, to run a javascript aware trace against the resulting xpcshell trace, we do:

chronisole.py jstrace path/to/xpcshell path/to/logfile -d 5

(-d controls the default maximum recurse depth, you can leave this off if you want, as the configuration file’s interesting logic can accomplish this more specifically)

Note that the results are basically a good proof-of-concept, but are not perfect.  What happens is that we find all calls to JS_NewContext in order to find out all the javascript contexts used in the system.  Then, we iterate over each of the context objects, watching for writes to their frame pointer (‘fp’).

Using some incorrect logic, we attempt to figure out what’s going on with the stack.  (Because of the save frame/restore frame stuff, the logic can get confused.)  What we should be doing is tracing the fp->down chain until we hit null each time, most likely.  But what we do is try and perform only 2 memory reads and figure out if we’re pushing or popping the stack.  If we are pushing, we look for calls made from the current point.

If the call is to the XPC call or getter/setter function, we do note the interface the call is going through and do some magic to go directly to the actual dispatch, avoiding the intermediary XPConnect stuff we could care less about.

Once we are in general trace space, we use the configuration file previously noted to figure out whether we want to display the current function (we do if it’s not boring), whether we want to recurse (we do if it’s interesting), and how deep we want to recurse.

The serious deficiency in this implementation is that we do not attempt to integrate the multiple javascript context interpretations into the same time-line.  Additionally, the function tracing is done independently of the known event stream for the context.  This means that if we go JS -> XPC -> CPP -> XPC -> JS, the trace we print won’t accurately represent that at all.  What we need to do is maintain a time-ordered series of known events that we populate and dispatch based off of this.  (This would also allow us to potentially be more memory efficient for large traces if we integrated on-demand fetching of more events.)

1 thought on “chroniquery

  1. Pingback: visophyte: data made shiny :: understanding libmime using chroniquery and unit tests

Comments are closed.