Mobile/Fennec/Android/Profiling: Difference between revisions

No edit summary
Line 619: Line 619:


= systemtap =
= systemtap =
== Compiling systemtap ==
Think of the children, get the [http://www.jnchen.com/_media/projects/mozilla/moz-systemtap-1.5.tar.bz2 prebuilt version]. Extract it to your host machine, and to /data on your device.
Basically running systemtap consists of the host portion and the target portion. For the host portion you use a standard systemtap installation to cross compile your script into a kernel module. For the target portion you transfer the kernel module to your device and run it on the device.
The host portion you can get through apt-get. The target portion you have to compile (use the [http://www.codesourcery.com/sgpp/lite/arm/portal/subscription?@template=lite CodeSourcery ARM GNU/Linux toolchain]):
LDFLAGS=-static \
  ac_cv_file__usr_include_avahi_common=no \
  ac_cv_file__usr_include_avahi_client=no \
  ac_cv_file__usr_include_nspr=no \
  ac_cv_file__usr_include_nspr4=no \
  ac_cv_file__usr_include_nss=no \
  ac_cv_file__usr_include_nss3=no \
  ./configure --prefix=/data/systemtap --host=arm-none-linux-gnueabi \
    --disable-translator --disable-nls
make install
Note that prefix has to be /data/systemtap (or another path that matches Android file system) so you need to make a /data directory on your host machine before you run make install.
You might need libelf. Get the latest elfutils release, then compile:
./configure --host=arm-none-linux-gnueabi --disable-nls \
  --prefix=/PATH/TO/TOOLCHAIN/arm-none-linux-gnueabi/libc/usr
cd libelf
make install
Where /PATH/TO/TOOLCHAIN is the path to your CodeSourcery toolchain.
== Using systemtap ==
Before you start, you need the source of your device's kernel. See the oprofile section for one example way to get the kernel source.
To compile your script on the host, make sure you have the NDK toolchain (r5 or above), then run
stap -gv -a arm -B CROSS_COMPILE=arm-linux-androideabi- \
  -r /PATH/TO/KERNEL -R /PATH/TO/SYSTEMTAP/RUNTIME \
  -m NAME -B CFLAGS_MODULE="-DMODULE -fno-pic" NAME.stp
where NAME is the name of your script, /PATH/TO/KERNEL is the path to your kernel source, and /PATH/TO/SYSTEMTAP/RUNTIME is the path to your target systemtap runtime (for example if you're using the prebuilt version above, /PATH/TO/SYSTEMTAP/RUNTIME = systemtap/share/systemtap/runtime)
adb push your kernel module to your device, then run
staprun -x PID NAME.ko
where PID is the pid of fennec process
== Sample systemtap scripts ==
=== glandium's I/O tracking script ===
global targetpid;
global file_path;
probe begin {
  targetpid = target();
}
probe kernel.function("__do_page_cache_readahead") {
  if (targetpid == pid())
    file_path[tid()] = d_path(&$filp->f_path);
}
probe kernel.function("do_mpage_readpage") {
  if (targetpid == pid() && (tid() in file_path)) {
    now = gettimeofday_us();
    printf("%d %s %d\n", now, file_path[tid()], $page->index*4096);
  }
}
probe kernel.function("__do_page_cache_readahead").return {
  if (targetpid == pid())
    delete file_path[tid()];
}
=== jchen's I/O tracking script ===
Note that this script accesses the hardware timer directly to overcome limitations in the systemtap gettimeofday() resolution (basically in systemtap, gettimeofday() is limited to jiffy resolution).
This also means to run it on a processor other than a MSM7x30, get_rawtime() will need to change.
%{
#include <arch/arm/include/asm/io.h>
#define MSM_CSR_BASE ((void*)0xF8001000)
#define MSM_TMR_BASE MSM_CSR_BASE
#define MSM_GPT_BASE (MSM_TMR_BASE + 0x04)
#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24)
#define TIMER_COUNT_VAL 0x0004
%}
function get_rawtime:long () %{ /* pure */ /* unprivileged */
  cycles_t c = readl(MSM_DGT_BASE + TIMER_COUNT_VAL);
  THIS->__retvalue = ((int64_t) (int32_t) c) - global.s_origin_time;
%}
global parentid, childid;
global start_time, total_time;
global origin_time;
global creation_time;
probe begin {
  parentid = 0;
  childid = 0;
  origin_time = get_rawtime()
}
function is_parent:long () {
  if (pid() == parentid)
    return parentid;
  if (0 == parentid && isinstr(execname(), "fennec")) {
    parentid = pid();
    return parentid;
  }
  return 0;
}
function is_child:long () {
  if (pid() == childid)
    return childid;
  if (0 == childid && isinstr(execname(), "plugin")) {
    childid = pid();
    return childid;
  }
  return 0;
}
probe syscall.fork.return {
  if (is_parent() || is_child()) {
    if ($return != 0) {
      thdid = is_parent() ? $return : -$return;
      creation_time[thdid] = get_rawtime();
      total_time[thdid, "", creation_time[thdid] / 8192] = -1;
    }
  }
}
probe syscall.execve.return {
  if (is_parent() || is_child()) {
    thdid = is_parent() ? tid() : -tid();
    creation_time[thdid] = get_rawtime();
    total_time[thdid, "", creation_time[thdid] / 8192] = -1;
  }
}
probe syscall.exit {
  if (is_parent() || is_child()) {
    thdid = is_parent() ? tid() : -tid();
    total_time[thdid, "", creation_time[thdid] / 8192] =
        get_rawtime() - creation_time[thdid];
  }
}
probe vfs.read {
  if (bytes_to_read > 0 && (is_parent() || is_child())) {
    thdid = is_parent() ? tid() : -tid();
    start_time[thdid] = get_rawtime();
  }
}
probe vfs.read.return {
  if (bytes_to_read > 0 && (is_parent() || is_child())) {
    thdid = is_parent() ? tid() : -tid();
    fn = "rd:" . __file_filename(file);
    st = start_time[thdid];
    total_time[thdid, fn, st / 8192] += get_rawtime() - st;
  }
}
probe kernel.function("filemap_fault") {
  if (is_parent() || is_child()) {
    thdid = is_parent() ? tid() : -tid();
    start_time[thdid] = get_rawtime();
  }
}
probe kernel.function("filemap_fault").return {
  if (is_parent() || is_child()) {
    thdid = is_parent() ? tid() : -tid();
    fn = "mm:" . __file_filename($vma->vm_file);
    st = start_time[thdid];
    total_time[thdid, fn, st / 8192] += get_rawtime() - st;
  }
}
probe end {
  foreach ([t, fn, st] in total_time) {
    printf("%d, %s, %d, %d\n", t, fn, st, total_time[t, fn, st]);
  }
}
Confirmed users
507

edits