[util] Add cairo-trace.

This tool can be used to trace all the cairo function calls made by an
applications.  This is useful for either extracting a test case triggering
a bug from an application, or simply to get a general idea of how an
application is using cairo.

After make install, cairo-trace program arguments, will print out all the
cairo calls to the terminal and also capture theme in ./program.$pid.trace

The format of the output is CairoScript, watch this space for more
cairo-script tools!
This commit is contained in:
Chris Wilson 2008-10-31 15:37:58 +00:00
parent 992f74d884
commit c554f18d78
9 changed files with 3763 additions and 8 deletions

View file

@ -10,6 +10,8 @@ AM_CONDITIONAL(CROSS_COMPILING, test "x$cross_compiling" = "xyes")
CAIRO_BIGENDIAN
CAIRO_CHECK_NATIVE_ATOMIC_PRIMITIVES
CAIRO_CHECK_ATOMIC_OP_NEEDS_MEMORY_BARRIER
AC_CHECK_SIZEOF(void *)
AC_CHECK_SIZEOF(size_t)
AC_MSG_CHECKING([for native Win32])
case "$host" in

View file

@ -480,6 +480,21 @@ AM_CONDITIONAL(BUILD_ANY2PPM,
-o "x$any2ppm_pdf" = "xyes" \
-o "x$any2ppm_ps" = "xyes")
dnl ===========================================================================
dnl The tracing utility requires LD_PRELOAD, so only build it for systems
dnl that are known to work.
case $host in
*-linux*|*-*bsd*)
have_ld_preload="yes"
;;
*)
have_ld_preload="no"
;;
esac
AM_CONDITIONAL(BUILD_TRACE, test "x$have_ld_preload" = "xyes")
dnl ===========================================================================
AC_ARG_ENABLE(some-floating-point,
@ -516,6 +531,8 @@ test/Makefile
test/pdiff/Makefile
perf/Makefile
util/Makefile
util/cairo-trace/Makefile
util/cairo-trace/cairo-trace
doc/Makefile
doc/public/Makefile
])

View file

@ -1,5 +1,11 @@
include $(top_srcdir)/build/Makefile.am.common
SUBDIRS = .
if BUILD_TRACE
SUBDIRS += cairo-trace
endif
util: malloc-stats.so backtrace-symbols.so
.la.so:

View file

@ -50,14 +50,6 @@ applications. This is useful for either extracting a test case triggering
a bug from an application, or simply to get a general idea of how an
application is using cairo.
This tool lives outside the cairo source distribution right now and can
be found in a git repository at:
http://cgit.freedesktop.org/~ickle/cairo-trace/
There are plans to move it to this directory in the future, and possibly
install it on the system.
cairo-api-update and xr2cairo
-----------------------------

View file

@ -0,0 +1,21 @@
bin_SCRIPTS = cairo-trace
lib_LTLIBRARIES = cairo-trace.la
AM_CPPFLAGS = -I$(top_srcdir)/src \
-I$(top_builddir)/src
cairo_trace_la_SOURCES = \
lookup-symbol.c \
lookup-symbol.h \
trace.c
cairo_trace_la_CFLAGS = @FREETYPE_CFLAGS@ @CAIRO_CFLAGS@
cairo_trace_la_LDFLAGS = -module -no-undefined
cairo_trace_la_LIBADD = -ldl -lz -lbfd
system-install:
grep -sq @libdir@/cairo-trace.so /etc/ld.so.preload || echo @libdir@/cairo-trace.so >> /etc/ld.so.preload
system-uninstall:
sed -e '/\/usr\/local\/lib\/cairo-trace.so/d' < /etc/ld.so.preload > /tmp/ld.so.preload && mv /tmp/ld.so.preload /etc/ld.so.preload;
EXTRA_DIST = cairo-trace.in

View file

@ -0,0 +1,58 @@
#!/bin/sh
prefix=@prefix@
exec_prefix=@exec_prefix@
nofile=
silent=
skip=1
while test $skip -eq 1; do
skip=0
case $1 in
--silent)
skip=1
silent=1
;;
--no-file)
skip=1
nofile=1
;;
esac
if test $skip -eq 1; then
shift
fi
done
if test $# -eq 0; then
cat << EOF
usage: cairo-trace [--no-file|--silent] command
cairo-trace will generate a log of all calls made by command to
cairo. This log will be stored in a file in the local directory
called command.pid.trace.
Whatever else happens is driven by its argument:
--silent - disables the overriding of stdout by cairo-trace.
The trace file is still generated, but your application
is free to continue to use stdout.
--no-file - disables the generation of an output file
Enviroment variables understood by cairo-trace:
CAIRO_TRACE_FLUSH - flush the output after every function call.
EOF
exit
fi
#echo $*
filename=""
if test -z "$nofile"; then
filename=`basename $1`.$$.trace
fi
if test -z "$filename"; then
LD_PRELOAD=@libdir@/cairo-trace.so CAIRO_TRACE_FD=3 LC_ALL=C $* 3>&1 >/dev/null
elif test -n "$silent"; then
LD_PRELOAD=@libdir@/cairo-trace.so LC_ALL=C CAIRO_TRACE_OUTFILE_EXACT=$filename $*
else
LD_PRELOAD=@libdir@/cairo-trace.so CAIRO_TRACE_FD=3 LC_ALL=C $* 3>&1 >/dev/null | tee $filename
fi

View file

@ -0,0 +1,290 @@
/* cairo-trace - a utility to record and replay calls to the Cairo library.
*
* Copyright © 2008 Chris Wilson
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*
* A less hacky utility to lookup the debug strings for a particular
* .text address.
* Derived from backtrace-symbols.c in cairo by Chris Wilson.
*/
/*
A hacky replacement for backtrace_symbols in glibc
backtrace_symbols in glibc looks up symbols using dladdr which is limited
in the symbols that it sees. libbacktracesymbols opens the executable and
shared libraries using libbfd and will look up backtrace information using
the symbol table and the dwarf line information.
It may make more sense for this program to use libelf instead of libbfd.
However, I have not investigated that yet.
Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
Copyright 2007 Jeff Muizelaar
*/
/* addr2line.c -- convert addresses to line number and function name
Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
This file was part of GNU Binutils.
*/
#define _GNU_SOURCE
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define true 1
#define false 0
#include "lookup-symbol.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <link.h>
#include <string.h>
#ifdef HAVE_LIBBFD
#include <bfd.h>
#include <libiberty.h>
struct symtab {
bfd *bfd;
asymbol **syms;
};
struct symbol {
int found;
bfd_vma pc;
struct symtab *symtab;
const char *filename;
const char *functionname;
unsigned int line;
};
static void
_symtab_fini (struct symtab *symtab)
{
if (symtab->syms != NULL)
free (symtab->syms);
if (symtab->bfd != NULL)
bfd_close (symtab->bfd);
}
/* Read in the symbol table. */
static int
_symtab_init (struct symtab *symtab, const char *filename)
{
char **matching;
long symcount;
unsigned int size;
symtab->bfd = NULL;
symtab->syms = NULL;
symtab->bfd = bfd_openr (filename, NULL);
if (symtab->bfd == NULL)
goto BAIL;
if (bfd_check_format (symtab->bfd, bfd_archive))
goto BAIL;
if (! bfd_check_format_matches (symtab->bfd, bfd_object, &matching))
goto BAIL;
symcount = bfd_read_minisymbols (symtab->bfd, false, (PTR) &symtab->syms, &size);
if (symcount == 0) {
symcount = bfd_read_minisymbols (symtab->bfd, true /* dynamic */ ,
(PTR) &symtab->syms, &size);
}
if (symcount < 0)
goto BAIL;
if ((bfd_get_file_flags (symtab->bfd) & HAS_SYMS) == 0)
goto BAIL;
return 1;
BAIL:
_symtab_fini (symtab);
return 0;
}
/* Look for an address in a section.
* This is called via bfd_map_over_sections.
*/
static void
find_address_in_section (bfd *abfd,
asection *section,
void *data)
{
bfd_vma vma;
bfd_size_type size;
struct symbol *symbol = data;
struct symtab *symtab = symbol->symtab;
if (symbol->found)
return;
if ((bfd_get_section_flags (symtab->bfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma (symtab->bfd, section);
if (symbol->pc < vma)
return;
size = bfd_section_size (symtab->bfd, section);
if (symbol->pc >= vma + size)
return;
symbol->found = bfd_find_nearest_line (symtab->bfd, section,
symtab->syms,
symbol->pc - vma,
&symbol->filename,
&symbol->functionname,
&symbol->line);
}
static void
_symbol_fini (struct symbol *symbol)
{
}
static void
_symbol_init (struct symbol *symbol, struct symtab *symtab, bfd_vma addr)
{
symbol->found = false;
symbol->symtab = symtab;
symbol->pc = addr;
}
static int
_symbol_print (struct symbol *symbol, char *buf, int buflen, const char *filename)
{
if (! symbol->found) {
buflen = snprintf (buf, buflen,
"[0x%llx] \?\?() \?\?:0",
(long long unsigned int) symbol->pc);
} else {
const char *name, *h;
char path[1024];
name = symbol->functionname;
if (name == NULL || *name == '\0')
name = "??";
if (symbol->filename != NULL)
filename = symbol->filename;
if (strcmp (filename, "/proc/self/exe") == 0) {
int len = readlink ("/proc/self/exe", path, sizeof (path) - 1);
if (len != -1) {
path[len] = '\0';
filename = path;
}
}
h = strrchr (filename, '/');
if (h != NULL)
filename = h + 1;
if (symbol->line) {
buflen = snprintf (buf, buflen, "%s (%s:%u)",
name, filename, symbol->line);
} else {
buflen = snprintf (buf, buflen, "%s (%s)", name, filename);
}
}
return buflen;
}
#endif
struct file_match {
const char *file;
ElfW(Addr) address;
ElfW(Addr) base;
void *hdr;
};
static int
find_matching_file (struct dl_phdr_info *info, size_t size, void *data)
{
struct file_match *match = data;
/* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
long n;
const ElfW(Phdr) *phdr;
ElfW(Addr) load_base = info->dlpi_addr;
phdr = info->dlpi_phdr;
for (n = info->dlpi_phnum; --n >= 0; phdr++) {
if (phdr->p_type == PT_LOAD) {
ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
if (match->address >= vaddr &&
match->address < vaddr + phdr->p_memsz)
{
/* we found a match */
match->file = info->dlpi_name;
match->base = info->dlpi_addr;
return 1;
}
}
}
return 0;
}
char *
lookup_symbol (char *buf, int buflen, const void *ptr)
{
struct file_match match;
#ifdef HAVE_LIBBFD
struct symtab symtab;
struct symbol symbol;
#endif
int i;
match.address = (ElfW(Addr)) ptr;
dl_iterate_phdr (find_matching_file, &match);
i = snprintf (buf, buflen,
"0x%llx",
(long long unsigned int) match.address);
if (match.file == NULL || *match.file == '\0')
match.file = "/proc/self/exe";
#ifdef HAVE_LIBBFD
if (! _symtab_init (&symtab, match.file))
return buf;
_symbol_init (&symbol, &symtab, match.address - match.base);
bfd_map_over_sections (symtab.bfd, find_address_in_section, &symbol);
if (symbol.found) {
buf[i++] = ' ';
_symbol_print (&symbol, buf + i, buflen - i, match.file);
}
_symbol_fini (&symbol);
_symtab_fini (&symtab);
#endif
return buf;
}

View file

@ -0,0 +1,24 @@
/* cairo-trace - a utility to record and replay calls to the Cairo library.
*
* Copyright © 2008 Chris Wilson
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOOKUP_SYMBOLS_H
char *
lookup_symbol (char *buf, int len, const void *ptr);
#endif /*LOOKUP_SYMBOLS_H */

3345
util/cairo-trace/trace.c Normal file

File diff suppressed because it is too large Load diff