mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-20 12:50:10 +01:00
[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:
parent
992f74d884
commit
c554f18d78
9 changed files with 3763 additions and 8 deletions
|
|
@ -10,6 +10,8 @@ AM_CONDITIONAL(CROSS_COMPILING, test "x$cross_compiling" = "xyes")
|
||||||
CAIRO_BIGENDIAN
|
CAIRO_BIGENDIAN
|
||||||
CAIRO_CHECK_NATIVE_ATOMIC_PRIMITIVES
|
CAIRO_CHECK_NATIVE_ATOMIC_PRIMITIVES
|
||||||
CAIRO_CHECK_ATOMIC_OP_NEEDS_MEMORY_BARRIER
|
CAIRO_CHECK_ATOMIC_OP_NEEDS_MEMORY_BARRIER
|
||||||
|
AC_CHECK_SIZEOF(void *)
|
||||||
|
AC_CHECK_SIZEOF(size_t)
|
||||||
|
|
||||||
AC_MSG_CHECKING([for native Win32])
|
AC_MSG_CHECKING([for native Win32])
|
||||||
case "$host" in
|
case "$host" in
|
||||||
|
|
|
||||||
17
configure.ac
17
configure.ac
|
|
@ -480,6 +480,21 @@ AM_CONDITIONAL(BUILD_ANY2PPM,
|
||||||
-o "x$any2ppm_pdf" = "xyes" \
|
-o "x$any2ppm_pdf" = "xyes" \
|
||||||
-o "x$any2ppm_ps" = "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 ===========================================================================
|
dnl ===========================================================================
|
||||||
|
|
||||||
AC_ARG_ENABLE(some-floating-point,
|
AC_ARG_ENABLE(some-floating-point,
|
||||||
|
|
@ -516,6 +531,8 @@ test/Makefile
|
||||||
test/pdiff/Makefile
|
test/pdiff/Makefile
|
||||||
perf/Makefile
|
perf/Makefile
|
||||||
util/Makefile
|
util/Makefile
|
||||||
|
util/cairo-trace/Makefile
|
||||||
|
util/cairo-trace/cairo-trace
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
doc/public/Makefile
|
doc/public/Makefile
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
include $(top_srcdir)/build/Makefile.am.common
|
include $(top_srcdir)/build/Makefile.am.common
|
||||||
|
|
||||||
|
SUBDIRS = .
|
||||||
|
|
||||||
|
if BUILD_TRACE
|
||||||
|
SUBDIRS += cairo-trace
|
||||||
|
endif
|
||||||
|
|
||||||
util: malloc-stats.so backtrace-symbols.so
|
util: malloc-stats.so backtrace-symbols.so
|
||||||
|
|
||||||
.la.so:
|
.la.so:
|
||||||
|
|
|
||||||
|
|
@ -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
|
a bug from an application, or simply to get a general idea of how an
|
||||||
application is using cairo.
|
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
|
cairo-api-update and xr2cairo
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
|
||||||
21
util/cairo-trace/Makefile.am
Normal file
21
util/cairo-trace/Makefile.am
Normal 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
|
||||||
58
util/cairo-trace/cairo-trace.in
Normal file
58
util/cairo-trace/cairo-trace.in
Normal 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
|
||||||
290
util/cairo-trace/lookup-symbol.c
Normal file
290
util/cairo-trace/lookup-symbol.c
Normal 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;
|
||||||
|
}
|
||||||
24
util/cairo-trace/lookup-symbol.h
Normal file
24
util/cairo-trace/lookup-symbol.h
Normal 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
3345
util/cairo-trace/trace.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue