diff options
Diffstat (limited to 'tools/jprof')
-rw-r--r-- | tools/jprof/README.html | 330 | ||||
-rw-r--r-- | tools/jprof/bfd.cpp | 231 | ||||
-rw-r--r-- | tools/jprof/coff.cpp | 99 | ||||
-rw-r--r-- | tools/jprof/elf.cpp | 133 | ||||
-rw-r--r-- | tools/jprof/intcnt.cpp | 71 | ||||
-rw-r--r-- | tools/jprof/intcnt.h | 38 | ||||
-rwxr-xr-x | tools/jprof/jprofsig | 46 | ||||
-rw-r--r-- | tools/jprof/leaky.cpp | 863 | ||||
-rw-r--r-- | tools/jprof/leaky.h | 122 | ||||
-rw-r--r-- | tools/jprof/moz.build | 28 | ||||
-rwxr-xr-x | tools/jprof/split-profile.py | 143 | ||||
-rw-r--r-- | tools/jprof/strset.cpp | 40 | ||||
-rw-r--r-- | tools/jprof/strset.h | 19 | ||||
-rw-r--r-- | tools/jprof/stub/Makefile.in | 8 | ||||
-rw-r--r-- | tools/jprof/stub/config.h | 18 | ||||
-rw-r--r-- | tools/jprof/stub/jprof.h | 17 | ||||
-rw-r--r-- | tools/jprof/stub/libmalloc.cpp | 790 | ||||
-rw-r--r-- | tools/jprof/stub/libmalloc.h | 45 | ||||
-rw-r--r-- | tools/jprof/stub/moz.build | 17 |
19 files changed, 0 insertions, 3058 deletions
diff --git a/tools/jprof/README.html b/tools/jprof/README.html deleted file mode 100644 index 2ae88dec4..000000000 --- a/tools/jprof/README.html +++ /dev/null @@ -1,330 +0,0 @@ -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<html> -<head><title>The Jprof Profiler</title></head> - -<body bgcolor="#FFFFFF" text="#000000" - link="#0000EE" vlink="#551A8B" alink="#FF0000"> -<center> -<h1>The Jprof Profiler</h1> -<font size="-1"> -<a href="mailto:jim_nance%yahoo.com">jim_nance@yahoo.com</a><p> -Recent (4/2011) updates Randell Jesup (see bugzilla for contact info) -</font> -<hr> - -<a href="#introduction">Introduction</a> | <a href="#operation">Operation</a> | -<a href="#setup">Setup</a> | <a href="#usage">Usage</a> | -<a href="#interpretation">Interpretation</a> - -</center> -<hr> - -<h3><a name="introduction">Introduction</a></h3> - -Jprof is a profiling tool. I am writing it because I need to find out -where mozilla is spending its time, and there do not seem to be any -profilers for Linux that can handle threads and/or shared libraries. -This code is based heavily on Kipp Hickman's leaky. - -<h3><a name="operation">Operation</a></h3> - -Jprof operates by installing a timer which periodically interrupts mozilla. -When this timer goes off, the jprof code inside mozilla walks the function call -stack to determine which code was executing and saves the results into the -<code>jprof-log</code> and <code>jprof-map</code> files. By collecting a large -number of these call stacks, it is possible to deduce where mozilla is spending -its time. - -<h3><a name="setup">Setup</a></h3> - -<p>Configure your mozilla with jprof support by adding -<code>--enable-jprof</code> to your configure options (eg adding -<code>ac_add_options --enable-jprof</code> to your <code>.mozconfig</code>) and -making sure that you do <strong>not</strong> have the -<code>--enable-strip</code> configure option set -- jprof needs symbols to -operate. On many architectures with GCC, you'll need to add -<code>--enable-optimize="-O3 -fno-omit-frame-pointer"</code> or the -equivalent to ensure frame pointer generation in the compiler you're using.</p> - -<p>Finally, build mozilla with your new configuration. Now you can run jprof.</p> - -<h3><a name="usage">Usage</a></h3> -<pre> jprof [-v] [-t] [-e exclude] [-i include] [-s stackdepth] [--last] [--all] [--start n [--end m]] [--output-dir dir] prog log [log2 ...]</pre> -Options: -<ul> - <li><b>-s depth</b> : Limit depth looked at from captured stack - frames</li> - <li><b>-v</b> : Output some information about the symbols, memory map, etc.</li> - <li><b>-t or --threads</b> : Group output according to thread. May require external - LD_PRELOAD library to help force sampling of spawned threads; jprof - may capture the main thread only. See <a - href="http://sam.zoy.org/writings/programming/gprof.html">gprof-helper</a>; - it may need adaption for jprof.</li> - <li><b>--only-thread id</b> : Only output data for thread 'id'</li> - <li><b>-e exclusion</b> : Allows excluding specific stack frames</li> - <li><b>-i inclusion</b> : Allows including specific stack frames</li> - <li><b>--last</b> : Only process data from the last 'section' of sampling - (starting at the last PROF)</li> - <li><b>--start N</b> : Start processing data at 'section' N </li> - <li><b>--end N</b> : Stop processing data at 'section' N </li> - <li><b>--output-dir dir</b> : Store generated .html files in the given directory </li> -</ul> -The behavior of jprof is determined by the value of the JPROF_FLAGS environment -variable. This environment variable can be composed of several substrings -which have the following meanings: -<ul> - <li> <b>JP_START</b> : Install the signal handler, and start sending the - timer signals. - - <li> <b>JP_DEFER</b> : Install the signal handler, but don't start sending - the timer signals. The user must start the signals by sending the first - one (with <code>kill -PROF</code>, or with <code>kill -ALRM</code> if - JP_REALTIME is used, or with <code>kill -POLL</code> (also known as <code>kill -IO</code>) if JP_RTC_HZ is used). - - <li> <b>JP_FIRST=x</b> : Wait x seconds before starting the timer - - <li> <b>JP_PERIOD=y</b> : Set timer to interrupt every y seconds. Only - values of y greater than or equal to 0.001 are supported. Default is - 0.050 (50ms). - - <li> <b>JP_REALTIME</b> : Do the profiling in intervals of real time rather - than intervals of time used by the mozilla process (and the kernel - when doing work for mozilla). This could probably lead to weird - results (you'll see whatever runs when mozilla is waiting for events), - but is needed to see time spent in the X server. - - <li> <b>JP_RTC_HZ=freq</b> : This option, only available on Linux if the - kernel is built with RTC support, makes jprof use the RTC timer instead of - using its own timer. This option, like JP_REALTIME, uses intervals of real - time. This option overrides JP_PERIOD. <code>freq</code> is the frequency - at which the timer should fire, measured in Hz. It must be a power of 2. - The maximal frequency allowed by the kernel can be changed by writing to - <code>/proc/sys/dev/rtc/max-user-freq</code>; the maximum value it can be - set to is 8192. Note that <code>/dev/rtc</code> will need to be readable - by the Firefox process; making that file world-readable is a simple way to - accomplish that. - - <li> <b>JP_CIRCULAR=size</b> : This tells jprof to store samples in a - circular buffer of the given size, which then will be saved (appended) - to disk when SIGUSR1 is received or JProfStopProfiling is done. If the - buffer overflows, the oldest entries will be evicted until there's - space for the new entry.<p> - - SIGUSR2 will cause the circular buffer to be cleared. - - <li> <b>JP_FILENAME=basefilename</b> : This is the filename used for - saving the log files to; the default is "jprof-log". If Electrolysis - is used, each process after the first will have the process ID - added ("jprof-log-3212"); - -</ul> - -<h4>Starting and stopping jprof from JavaScript</h4> -<p> -A build with jprof enabled adds four functions to the Window object:<p> -<code>JProfStartProfiling()</code> and <code>JProfStopProfiling()</code>: When used with JP_DEFER, these -allow one to start and stop the timer just around whatever critical section is -being profiled.</p><p> -<code>JProfClearCircular()</code> and <code>JProfSaveCircular()</code>: -These clear the circular buffer and save the buffer (without stopping), respectively.</p> - -<h4>Examples of JPROF_FLAGS usage</h4> -<ul> - - <li>To make the timer start firing 3 seconds after the program is started and - fire every 25 milliseconds of program time use: - <pre> - setenv JPROF_FLAGS "JP_START JP_FIRST=3 JP_PERIOD=0.025" </pre> - - <li>To make the timer start on your signal and fire every 1 millisecond of - program time use: - <pre> - setenv JPROF_FLAGS "JP_DEFER JP_PERIOD=0.001" </pre> - - <li>To make the timer start on your signal and fire every 10 milliseconds of - wall-clock time use: - <pre> - setenv JPROF_FLAGS "JP_DEFER JP_PERIOD=0.010 JP_REALTIME" </pre> - - <li>To make the timer start on your signal and fire at 8192 Hz in wall-clock - time use: - <pre> - setenv JPROF_FLAGS "JP_DEFER JP_RTC_HZ=8192" </pre> - - <li>To make the timer start on JProfStartProfiling() and run continously - with a 1ms sample rate until told to stop, then save the last 1MB of - data: - <pre> - setenv JPROF_FLAGS "JP_DEFER JP_CIRCULAR=1048576 JP_PERIOD=0.001" </pre> - -</ul> - -<h4>Pausing profiles</h4> - -<P>jprof can be paused at any time by sending a SIGUSR1 to mozilla (<code>kill --USR1</code>). This will cause the timer signals to stop and jprof-map to be -written, but it will not close jprof-log. Combining SIGUSR1 with the JP_DEFER -option allows profiling of one sequence of actions by starting the timer right -before starting the actions and stopping the timer right afterward. - -<P>After a SIGUSR1, sending another timer signal (SIGPROF, SIGALRM, or SIGPOLL (aka SIGIO), -depending on the mode) can be used to continue writing data to the same -output. - -<P>SIGUSR2 will cause the circular buffer to be cleared, if it's in use. -This is useful right before running a test when you're using a large, -continuous circular buffer, or programmatically at the start of an action -which might take too long (JProfClearCircular()). - -<h4>Looking at the results</h4> - -Now that we have <code>jprof-log</code> and <code>jprof-map</code> files, we -can use the jprof executable is used to turn them into readable output. To do -this jprof needs the name of the mozilla binary and the log file. It deduces -the name of the map file: - -<pre> - ./jprof /home/user/mozilla/objdir/dist/bin/firefox ./jprof-log > tmp.html -</pre> - -This will generate the file <code>tmp.html</code> which you should view in a -web browser. - -<pre> - ./jprof --output-dir=/tmp /home/user/mozilla/objdir/dist/bin/firefox ./jprof-log* -</pre> - -This will generate a set of files in /tmp for each process. - - -<h3><a name="interpretation">Interpretation</a></h3> - - -The Jprof output is split into a flat portion and a hierarchical portion. -There are links to each section at the top of the page. It is typically -easier to analyze the profile by starting with the flat output and following -the links contained in the flat output up to the hierarchical output. - -<h4><a name="flat">Flat output</a></h3> - -The flat portion of the profile indicates which functions were executing -when the timer was going off. It is displayed as a list of functions names -on the right and the number of times that function was interrupted on the -left. The list is sorted by decreasing interrupt count. For example: - -<blockquote> <pre> -Total hit count: 151603 -Count %Total Function Name - -<a href="#23081">8806 5.8 __libc_poll</a> -<a href="#40008">2254 1.5 __i686.get_pc_thunk.bx</a> -<a href="#21390">2053 1.4 _int_malloc</a> -<a href="#49013">1777 1.2 nsStyleContext::GetStyleData(nsStyleStructID)</a> -<a href="#21380">1600 1.1 __libc_malloc</a> -<a href="#603">1552 1.0 nsCOMPtr_base::~nsCOMPtr_base()</a> -</pre> </blockquote> - -This shows that of the 151603 times the timer fired, 1777 (1.2% of the total) were inside nsStyleContext::GetStyleData() and 1552 (1.0% of the total) were in the nsCOMPtr_base destructor. - -<p> -In general, the functions with the highest count are the functions which -are taking the most time. - -<P> -The function names are linked to the entry for that function in the -hierarchical profile, which is described in the next section. - -<h4><a name="hier">Hierarchical output</a></h4> - -The hierarchical output is divided up into sections, with each section -corresponding to one function. A typical section looks something like -this: - -<blockquote><pre> - index Count Hits Function Name - <A href="#72871"> 545 (46.4%) nsBlockFrame::ReflowInlineFrames(nsBlockReflowState&, nsLineList_iterator, int*)</A> - <A href="#72873"> 100 (8.5%) nsBlockFrame::ReflowDirtyLines(nsBlockReflowState&)</A> - 72870 4 (0.3%) <a name=72870> 645 (54.9%)</a> <b>nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState&, nsLineLayout&, nsLineList_iterator, nsFlowAreaRect&, int&, nsFloatManager::SavedState*, int*, LineReflowStatus*, int)</b> - <A href="#72821"> 545 (46.4%) nsBlockFrame::ReflowInlineFrame(nsBlockReflowState&, nsLineLayout&, nsLineList_iterator, nsIFrame*, LineReflowStatus*)</A> - <A href="#72853"> 83 (7.1%) nsBlockFrame::PlaceLine(nsBlockReflowState&, nsLineLayout&, nsLineList_iterator, nsFloatManager::SavedState*, nsRect&, int&, int*)</A> - <A href="#74150"> 9 (0.8%) nsLineLayout::BeginLineReflow(int, int, int, int, int, int)</A> - <A href="#74897"> 1 (0.1%) nsTextFrame::GetType() const</A> - <A href="#74131"> 1 (0.1%) nsLineLayout::RelativePositionFrames(nsOverflowAreas&)</A> - <A href="#58320"> 1 (0.1%) __i686.get_pc_thunk.bx</A> - <A href="#53077"> 1 (0.1%) PL_ArenaAllocate</A> -</pre></blockquote> - -The information this block tells us is: - -<ul> -<li>There were 4 profiler hits <em>in</em> <code>nsBlockFrame::DoReflowInlineFrames</code> -<li>There were 645 profiler hits <em>in or under</em> <code>nsBlockFrame::DoReflowInlineFrames</code>. Of these: -<ul> - <li>545 were in or under <code>nsBlockFrame::ReflowInlineFrame</code> - <li>83 were in or under <code>nsBlockFrame::PlaceLine</code> - <li>9 were in or under <code>nsLineLayout::BeginLineReflow</code> - <li>1 was in or under <code>nsTextFrame::GetType</code> - <li>1 was in or under <code>nsLineLayout::RelativePositionFrames</code> - <li>1 was in or under <code>__i686.get_pc_thunk.bx</code> - <li>1 was in or under <code>PL_ArenaAllocate</code> -</ul> -<li>Of these 645 calls into <code>nsBlockFrame::DoReflowInlineFrames</code>: -<ul> - <li>545 came from <code>nsBlockFrame::ReflowInlineFrames</code> - <li>100 came from <code>nsBlockFrame::ReflowDirtyLines</code> -</ul> -</ul> - - -The rest of this section explains how to read this information off from the jprof output. - -<p>This block corresponds to the function <code>nsBlockFrame::DoReflowInlineFrames</code>, which is -therefore bolded and not a link. The name of this function is preceded by -five numbers which have the following meaning. The number on the left (72870) -is the index number, and is not important. The next number (4) and the -percentage following (0.3%) are the number -of times this function was interrupted by the timer and the percentage of -the total hits that is. The last number pair ("645 (54.9%)") -are the number of times this function was in the call stack when the timer went -off. That is, the timer went off while we were in code that was ultimately -called from <code>nsBlockFrame::DoReflowInlineFrames</code>. -<p>For our example we can see that our function was in the call stack for -645 interrupt ticks, but we were only the function that was running when -the interrupt arrived 4 times. -<P> -The functions listed above the line for <code>nsBlockFrame::DoReflowInlineFrames</code> are its -callers. The numbers to the left of these function names are the numbers of -times these functions were in the call stack as callers of -<code>nsBlockFrame::DoReflowInlineFrames</code>. In our example, we were called 545 times by -<code>nsBlockFrame::ReflowInlineFrames</code> and 100 times by -<code>nsBlockFrame::ReflowDirtyLines</code>. -<P> -The functions listed below the line for <code>nsBlockFrame::DoReflowInlineFrames</code> are its -callees. The numbers to the left of the function names are the numbers of -times these functions were in the callstack as callees of -<code>nsBlockFrame::DoReflowInlineFrames</code> and the corresponding percentages. In our example, of the 645 profiler hits under <code>nsBlockFrame::DoReflowInlineFrames</code> 545 were under <code>nsBlockFrame::ReflowInlineFrame</code>, 83 were under <code>nsBlockFrame::PlaceLine</code>, and so forth.<p> - -<b>NOTE:</b> If there are loops of execution or recursion, the numbers will -not add up and percentages can exceed 100%. If a function directly calls -itself "(self)" will be appended to the line, but indirect recursion will -not be marked. - -<h3>Bugs</h3> -The current build of Jprof has only been tested under Ubuntu 8.04 LTS, but -should work under any fairly modern linux distribution using GCC/GLIBC. -Please update this document with any known compatibilities/incompatibilities. -<p> -If you get an error:<p><code>Inconsistency detected by ld.so: dl-open.c: 260: dl_open_worker: Assertion `_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT' failed! -</code><p>that means you've hit a timing hole in the version of glibc you're -running. See <a -href="http://sources.redhat.com/bugzilla/show_bug.cgi?id=4578">Redhat bug 4578</a>. -<!-- <h3>Update</h3> -<ul> -</ul> ---> - -</body> -</html> diff --git a/tools/jprof/bfd.cpp b/tools/jprof/bfd.cpp deleted file mode 100644 index 2e013d0ae..000000000 --- a/tools/jprof/bfd.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// vim:ts=8:sw=2:et: -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "leaky.h" - -#ifdef USE_BFD -#include <stdio.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <libgen.h> -#include <bfd.h> -#include <cxxabi.h> - -static bfd *try_debug_file(const char *filename, unsigned long crc32) -{ - int fd = open(filename, O_RDONLY); - if (fd < 0) - return nullptr; - - unsigned char buf[4*1024]; - unsigned long crc = 0; - - while (1) { - ssize_t count = read(fd, buf, sizeof(buf)); - if (count <= 0) - break; - - crc = bfd_calc_gnu_debuglink_crc32(crc, buf, count); - } - - close(fd); - - if (crc != crc32) - return nullptr; - - bfd *object = bfd_openr(filename, nullptr); - if (!bfd_check_format(object, bfd_object)) { - bfd_close(object); - return nullptr; - } - - return object; -} - -static bfd *find_debug_file(bfd *lib, const char *aFileName) -{ - // check for a separate debug file with symbols - asection *sect = bfd_get_section_by_name(lib, ".gnu_debuglink"); - - if (!sect) - return nullptr; - - bfd_size_type debuglinkSize = bfd_section_size (objfile->obfd, sect); - - char *debuglink = new char[debuglinkSize]; - bfd_get_section_contents(lib, sect, debuglink, 0, debuglinkSize); - - // crc checksum is aligned to 4 bytes, and after the NUL. - int crc_offset = (int(strlen(debuglink)) & ~3) + 4; - unsigned long crc32 = bfd_get_32(lib, debuglink + crc_offset); - - // directory component - char *dirbuf = strdup(aFileName); - const char *dir = dirname(dirbuf); - - static const char debug_subdir[] = ".debug"; - // This is gdb's default global debugging info directory, but gdb can - // be instructed to use a different directory. - static const char global_debug_dir[] = "/usr/lib/debug"; - - char *filename = - new char[strlen(global_debug_dir) + strlen(dir) + crc_offset + 3]; - - // /path/debuglink - sprintf(filename, "%s/%s", dir, debuglink); - bfd *debugFile = try_debug_file(filename, crc32); - if (!debugFile) { - - // /path/.debug/debuglink - sprintf(filename, "%s/%s/%s", dir, debug_subdir, debuglink); - debugFile = try_debug_file(filename, crc32); - if (!debugFile) { - - // /usr/lib/debug/path/debuglink - sprintf(filename, "%s/%s/%s", global_debug_dir, dir, debuglink); - debugFile = try_debug_file(filename, crc32); - } - } - - delete[] filename; - free(dirbuf); - delete[] debuglink; - - return debugFile; -} - - -// Use an indirect array to avoid copying tons of objects -Symbol ** leaky::ExtendSymbols(int num) -{ - long n = numExternalSymbols + num; - - externalSymbols = (Symbol**) - realloc(externalSymbols, - (size_t) (sizeof(externalSymbols[0]) * n)); - Symbol *new_array = new Symbol[n]; - for (int i = 0; i < num; i++) { - externalSymbols[i + numExternalSymbols] = &new_array[i]; - } - lastSymbol = externalSymbols + n; - Symbol **sp = externalSymbols + numExternalSymbols; - numExternalSymbols = n; - return sp; -} - -#define NEXT_SYMBOL do { sp++; \ - if (sp >= lastSymbol) { \ - sp = ExtendSymbols(16384); \ - } \ - } while (0) - -void leaky::ReadSymbols(const char *aFileName, u_long aBaseAddress) -{ - int initialSymbols = usefulSymbols; - if (nullptr == externalSymbols) { - externalSymbols = (Symbol**) calloc(sizeof(Symbol*),10000); - Symbol *new_array = new Symbol[10000]; - for (int i = 0; i < 10000; i++) { - externalSymbols[i] = &new_array[i]; - } - numExternalSymbols = 10000; - } - Symbol** sp = externalSymbols + usefulSymbols; - lastSymbol = externalSymbols + numExternalSymbols; - - // Create a dummy symbol for the library so, if it doesn't have any - // symbols, we show it by library. - (*sp)->Init(aFileName, aBaseAddress); - NEXT_SYMBOL; - - bfd_boolean kDynamic = (bfd_boolean) false; - - static int firstTime = 1; - if (firstTime) { - firstTime = 0; - bfd_init (); - } - - bfd* lib = bfd_openr(aFileName, nullptr); - if (nullptr == lib) { - return; - } - if (!bfd_check_format(lib, bfd_object)) { - bfd_close(lib); - return; - } - - bfd *symbolFile = find_debug_file(lib, aFileName); - - // read mini symbols - PTR minisyms; - unsigned int size; - long symcount = 0; - - if (symbolFile) { - symcount = bfd_read_minisymbols(symbolFile, kDynamic, &minisyms, &size); - if (symcount == 0) { - bfd_close(symbolFile); - } else { - bfd_close(lib); - } - } - if (symcount == 0) { - symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); - if (symcount == 0) { - // symtab is empty; try dynamic symbols - kDynamic = (bfd_boolean) true; - symcount = bfd_read_minisymbols(lib, kDynamic, &minisyms, &size); - } - symbolFile = lib; - } - - asymbol* store; - store = bfd_make_empty_symbol(symbolFile); - - // Scan symbols - size_t demangle_buffer_size = 128; - char *demangle_buffer = (char*) malloc(demangle_buffer_size); - bfd_byte* from = (bfd_byte *) minisyms; - bfd_byte* fromend = from + symcount * size; - for (; from < fromend; from += size) { - asymbol *sym; - sym = bfd_minisymbol_to_symbol(symbolFile, kDynamic, (const PTR) from, store); - - symbol_info syminfo; - bfd_get_symbol_info (symbolFile, sym, &syminfo); - -// if ((syminfo.type == 'T') || (syminfo.type == 't')) { - const char* nm = bfd_asymbol_name(sym); - if (nm && nm[0]) { - char* dnm = nullptr; - if (strncmp("__thunk", nm, 7)) { - dnm = - abi::__cxa_demangle(nm, demangle_buffer, &demangle_buffer_size, 0); - if (dnm) { - demangle_buffer = dnm; - } - } - (*sp)->Init(dnm ? dnm : nm, syminfo.value + aBaseAddress); - NEXT_SYMBOL; - } -// } - } - - free(demangle_buffer); - demangle_buffer = nullptr; - - bfd_close(symbolFile); - - int interesting = sp - externalSymbols; - if (!quiet) { - printf("%s provided %d symbols\n", aFileName, - interesting - initialSymbols); - } - usefulSymbols = interesting; -} - -#endif /* USE_BFD */ diff --git a/tools/jprof/coff.cpp b/tools/jprof/coff.cpp deleted file mode 100644 index 78aa26733..000000000 --- a/tools/jprof/coff.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "leaky.h" - -#ifdef USE_COFF - -#define LANGUAGE_C -#include <sym.h> -#include <cmplrs/stsupport.h> -#include <symconst.h> -#include <filehdr.h> -#include <ldfcn.h> -#include <string.h> -#include <stdlib.h> - -#ifdef IRIX4 -extern "C" { - extern char *demangle(char const* in); -}; -#else -#include <dem.h> -#endif - -static char *Demangle(char *rawName) -{ -#ifdef IRIX4 - return strdup(demangle(rawName)); -#else - char namebuf[4000]; - demangle(rawName, namebuf); - return strdup(namebuf); -#endif -} - -void leaky::readSymbols(const char *fileName) -{ - LDFILE *ldptr; - - ldptr = ldopen(fileName, nullptr); - if (!ldptr) { - fprintf(stderr, "%s: unable to open \"%s\"\n", applicationName, - fileName); - exit(-1); - } - if (PSYMTAB(ldptr) == 0) { - fprintf(stderr, "%s: \"%s\": has no symbol table\n", applicationName, - fileName); - exit(-1); - } - - long isymMax = SYMHEADER(ldptr).isymMax; - long iextMax = SYMHEADER(ldptr).iextMax; - long iMax = isymMax + iextMax; - - long alloced = 10000; - Symbol* syms = (Symbol*) malloc(sizeof(Symbol) * 10000); - Symbol* sp = syms; - Symbol* last = syms + alloced; - SYMR symr; - - for (long isym = 0; isym < iMax; isym++) { - if (ldtbread(ldptr, isym, &symr) != SUCCESS) { - fprintf(stderr, "%s: can't read symbol #%d\n", applicationName, - isym); - exit(-1); - } - if (isym < isymMax) { - if ((symr.st == stStaticProc) - || ((symr.st == stProc) && - ((symr.sc == scText) || (symr.sc == scAbs))) - || ((symr.st == stBlock) && - (symr.sc == scText))) { - // Text symbol. Set name field to point to the symbol name - sp->name = Demangle(ldgetname(ldptr, &symr)); - sp->address = symr.value; - sp++; - if (sp >= last) { - long n = alloced + 10000; - syms = (Symbol*) - realloc(syms, (size_t) (sizeof(Symbol) * n)); - last = syms + n; - sp = syms + alloced; - alloced = n; - } - } - } - } - - int interesting = sp - syms; - if (!quiet) { - printf("Total of %d symbols\n", interesting); - } - usefulSymbols = interesting; - externalSymbols = syms; -} - -#endif /* USE_COFF */ diff --git a/tools/jprof/elf.cpp b/tools/jprof/elf.cpp deleted file mode 100644 index 1de1d2dcc..000000000 --- a/tools/jprof/elf.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "leaky.h" - -#ifdef USE_ELF - -#include "leaky.h" -#include <stdio.h> -#include <malloc.h> -#include <libelf/libelf.h> -#include <unistd.h> -#include <fcntl.h> -#include <string.h> - -void leaky::readSymbols(const char *fileName) -{ - int fd = ::open(fileName, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "%s: unable to open \"%s\"\n", applicationName, - fileName); - exit(-1); - } - - elf_version(EV_CURRENT); - Elf *elf = elf_begin(fd, ELF_C_READ, 0); - if (!elf) { - fprintf(stderr, "%s: \"%s\": has no symbol table\n", applicationName, - fileName); - exit(-1); - } - - long alloced = 10000; - Symbol* syms = (Symbol*) malloc(sizeof(Symbol) * 10000); - Symbol* sp = syms; - Symbol* last = syms + alloced; - - // Get each of the relevant sections and add them to the list of - // symbols. - Elf32_Ehdr *ehdr = elf32_getehdr(elf); - if (!ehdr) { - fprintf(stderr, "%s: elf library lossage\n", applicationName); - exit(-1); - } -#if 0 - Elf32_Half ndx = ehdr->e_shstrndx; -#endif - - Elf_Scn *scn = 0; - int strtabndx = -1; - for (int i = 1; (scn = elf_nextscn(elf, scn)) != 0; i++) { - Elf32_Shdr *shdr = elf32_getshdr(scn); -#if 0 - char *name = elf_strptr(elf, ndx, (size_t) shdr->sh_name); - printf("Section %s (%d 0x%x)\n", name ? name : "(null)", - shdr->sh_type, shdr->sh_type); -#endif - if (shdr->sh_type == SHT_STRTAB) { - /* We assume here that string tables preceed symbol tables... */ - strtabndx = i; - continue; - } -#if 0 - if (shdr->sh_type == SHT_DYNAMIC) { - /* Dynamic */ - Elf_Data *data = elf_getdata(scn, 0); - if (!data || !data->d_size) { - printf("No data..."); - continue; - } - - Elf32_Dyn *dyn = (Elf32_Dyn*) data->d_buf; - Elf32_Dyn *lastdyn = - (Elf32_Dyn*) ((char*) data->d_buf + data->d_size); - for (; dyn < lastdyn; dyn++) { - printf("tag=%d value=0x%x\n", dyn->d_tag, dyn->d_un.d_val); - } - } else -#endif - if ((shdr->sh_type == SHT_SYMTAB) || - (shdr->sh_type == SHT_DYNSYM)) { - /* Symbol table */ - Elf_Data *data = elf_getdata(scn, 0); - if (!data || !data->d_size) { - printf("No data..."); - continue; - } - - /* In theory we now have the symbols... */ - Elf32_Sym *esym = (Elf32_Sym*) data->d_buf; - Elf32_Sym *lastsym = - (Elf32_Sym*) ((char*) data->d_buf + data->d_size); - for (; esym < lastsym; esym++) { -#if 0 - char *nm = elf_strptr(elf, strtabndx, (size_t)esym->st_name); - printf("%20s 0x%08x %02x %02x\n", - nm, esym->st_value, ELF32_ST_BIND(esym->st_info), - ELF32_ST_TYPE(esym->st_info)); -#endif - if ((esym->st_value == 0) || - (ELF32_ST_BIND(esym->st_info) == STB_WEAK) || - (ELF32_ST_BIND(esym->st_info) == STB_NUM) || - (ELF32_ST_TYPE(esym->st_info) != STT_FUNC)) { - continue; - } -#if 1 - char *nm = elf_strptr(elf, strtabndx, (size_t)esym->st_name); -#endif - sp->name = nm ? strdup(nm) : "(no name)"; - sp->address = esym->st_value; - sp++; - if (sp >= last) { - long n = alloced + 10000; - syms = (Symbol*) - realloc(syms, (size_t) (sizeof(Symbol) * n)); - last = syms + n; - sp = syms + alloced; - alloced = n; - } - } - } - } - - int interesting = sp - syms; - if (!quiet) { - printf("Total of %d symbols\n", interesting); - } - usefulSymbols = interesting; - externalSymbols = syms; -} - -#endif /* USE_ELF */ diff --git a/tools/jprof/intcnt.cpp b/tools/jprof/intcnt.cpp deleted file mode 100644 index d0f7e1f33..000000000 --- a/tools/jprof/intcnt.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "intcnt.h" - -IntCount::IntCount() : numInts(0), iPair(nullptr) { } -IntCount::~IntCount() { delete [] iPair;} -int IntCount::getSize() {return numInts;} -int IntCount::getCount(int pos) {return iPair[pos].cnt;} -int IntCount::getIndex(int pos) {return iPair[pos].idx;} - -void IntCount::clear() -{ - delete[] iPair; - iPair = new IntPair[0]; - numInts = 0; -} - -int IntCount::countAdd(int index, int increment) -{ - if(numInts) { - // Do a binary search to find the element - int divPoint = 0; - - if(index>iPair[numInts-1].idx) { - divPoint = numInts; - } else if(index<iPair[0].idx) { - divPoint = 0; - } else { - int low=0, high=numInts-1; - int mid = (low+high)/2; - while(1) { - mid = (low+high)/2; - - if(index<iPair[mid].idx) { - high = mid; - } else if(index>iPair[mid].idx) { - if(mid<numInts-1 && index<iPair[mid+1].idx) { - divPoint = mid+1; - break; - } else { - low = mid+1; - } - } else if(index==iPair[mid].idx) { - return iPair[mid].cnt += increment; - } - } - } - - int i; - IntPair *tpair = new IntPair[numInts+1]; - for(i=0; i<divPoint; i++) { - tpair[i] = iPair[i]; - } - for(i=divPoint; i<numInts; i++) { - tpair[i+1] = iPair[i]; - } - ++numInts; - delete [] iPair; - iPair = tpair; - iPair[divPoint].idx = index; - iPair[divPoint].cnt = increment; - return increment; - } else { - iPair = new IntPair[1]; - numInts = 1; - iPair[0].idx = index; - return iPair[0].cnt = increment; - } -} diff --git a/tools/jprof/intcnt.h b/tools/jprof/intcnt.h deleted file mode 100644 index 3c009eac2..000000000 --- a/tools/jprof/intcnt.h +++ /dev/null @@ -1,38 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef INTCNT_H -#define INTCNT_H - -class IntCount -{ -public: - IntCount(); - ~IntCount(); - void clear(); - int countAdd(int index, int increment=1); - int countGet(int index); - int getSize(); - int getCount(int pos); - int getIndex(int pos); - - IntCount(const IntCount&old) - { - numInts = old.numInts; - if (numInts > 0) { - iPair = new IntPair[numInts]; - for (int i = 0; i < numInts; i++) { - iPair[i] = old.iPair[i]; - } - } else { - iPair = nullptr; - } - } -private: - - int numInts; - struct IntPair{int idx; int cnt;} *iPair; -}; - -#endif diff --git a/tools/jprof/jprofsig b/tools/jprof/jprofsig deleted file mode 100755 index 02226fc4b..000000000 --- a/tools/jprof/jprofsig +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# -# Find Mozilla PID and send it a signal, to be used -# with the jprof tool. -# - -jpsignal_usage() { - echo "Usage: jprofsig [start|stop]" - exit 1 -} - -if [ $# != 1 ]; then - echo "Wrong number of arguments." - jpsignal_usage -fi - -jpsignal_arg="$1" - -# Find & print mozilla PID -tmpmoz=`ps aux | grep mozilla-bin | head -1 | awk '{ print $2 }'` -echo "Mozilla PID = $tmpmoz" - -# See how we were called. -case "$jpsignal_arg" in - start) - if [ "$JP_REALTIME" = 1 ]; then - kill -ALRM $tmpmoz - else - # Normal, non-realtime mode. - kill -PROF $tmpmoz - fi - ;; - stop) - kill -USR1 $tmpmoz - ;; - *) - jpsignal_usage - exit 1 -esac - -exit 0 diff --git a/tools/jprof/leaky.cpp b/tools/jprof/leaky.cpp deleted file mode 100644 index d8e5322f5..000000000 --- a/tools/jprof/leaky.cpp +++ /dev/null @@ -1,863 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "leaky.h" -#include "intcnt.h" - -#include <sys/types.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#ifndef NTO -#include <getopt.h> -#endif -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> - -#ifdef NTO -#include <mem.h> -#endif - -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif - -static const u_int DefaultBuckets = 10007; // arbitrary, but prime -static const u_int MaxBuckets = 1000003; // arbitrary, but prime - -//---------------------------------------------------------------------- - -int main(int argc, char** argv) -{ - leaky* l = new leaky; - - l->initialize(argc, argv); - l->outputfd = stdout; - - for (int i = 0; i < l->numLogFiles; i++) { - if (l->output_dir || l->numLogFiles > 1) { - char name[2048]; // XXX fix - if (l->output_dir) - snprintf(name,sizeof(name),"%s/%s.html",l->output_dir,argv[l->logFileIndex + i]); - else - snprintf(name,sizeof(name),"%s.html",argv[l->logFileIndex + i]); - - fprintf(stderr,"opening %s\n",name); - l->outputfd = fopen(name,"w"); - // if an error we won't process the file - } - if (l->outputfd) { // paranoia - l->open(argv[l->logFileIndex + i]); - - if (l->outputfd != stderr) { - fclose(l->outputfd); - l->outputfd = nullptr; - } - } - } - - return 0; -} - -char * -htmlify(const char *in) -{ - const char *p = in; - char *out, *q; - int n = 0; - size_t newlen; - - // Count the number of '<' and '>' in the input. - while ((p = strpbrk(p, "<>"))) - { - ++n; - ++p; - } - - // Knowing the number of '<' and '>', we can calculate the space - // needed for the output string. - newlen = strlen(in) + n * 3 + 1; - out = new char[newlen]; - - // Copy the input to the output, with substitutions. - p = in; - q = out; - do - { - if (*p == '<') - { - strcpy(q, "<"); - q += 4; - } - else if (*p == '>') - { - strcpy(q, ">"); - q += 4; - } - else - { - *q++ = *p; - } - p++; - } while (*p); - *q = '\0'; - - return out; -} - -leaky::leaky() -{ - applicationName = nullptr; - progFile = nullptr; - - quiet = true; - showAddress = false; - showThreads = false; - stackDepth = 100000; - onlyThread = 0; - cleo = false; - - mappedLogFile = -1; - firstLogEntry = lastLogEntry = 0; - - sfd = -1; - externalSymbols = 0; - usefulSymbols = 0; - numExternalSymbols = 0; - lowestSymbolAddr = 0; - highestSymbolAddr = 0; - - loadMap = nullptr; - - collect_last = false; - collect_start = -1; - collect_end = -1; -} - -leaky::~leaky() -{ -} - -void leaky::usageError() -{ - fprintf(stderr, "Usage: %s [-v] [-t] [-e exclude] [-i include] [-s stackdepth] [--last] [--all] [--start n [--end m]] [--cleo] [--output-dir dir] prog log [log2 ...]\n", (char*) applicationName); - fprintf(stderr, - "\t-v: verbose\n" - "\t-t | --threads: split threads\n" - "\t--only-thread n: only profile thread N\n" - "\t-i include-id: stack must include specified id\n" - "\t-e exclude-id: stack must NOT include specified id\n" - "\t-s stackdepth: Limit depth looked at from captured stack frames\n" - "\t--last: only profile the last capture section\n" - "\t--start n [--end m]: profile n to m (or end) capture sections\n" - "\t--cleo: format output for 'cleopatra' display\n" - "\t--output-dir dir: write output files to dir\n" - "\tIf there's one log, output goes to stdout unless --output-dir is set\n" - "\tIf there are more than one log, output files will be named with .html added\n" - ); - exit(-1); -} - -static struct option longopts[] = { - { "threads", 0, nullptr, 't' }, - { "only-thread", 1, nullptr, 'T' }, - { "last", 0, nullptr, 'l' }, - { "start", 1, nullptr, 'x' }, - { "end", 1, nullptr, 'n' }, - { "cleo",0, nullptr, 'c' }, - { "output-dir", 1, nullptr, 'd' }, - { nullptr, 0, nullptr, 0 }, -}; - -void leaky::initialize(int argc, char** argv) -{ - applicationName = argv[0]; - applicationName = strrchr(applicationName, '/'); - if (!applicationName) { - applicationName = argv[0]; - } else { - applicationName++; - } - - int arg; - int errflg = 0; - int longindex = 0; - - onlyThread = 0; - output_dir = nullptr; - cleo = false; - - // XXX tons of cruft here left over from tracemalloc - // XXX The -- options shouldn't need short versions, or they should be documented - while (((arg = getopt_long(argc, argv, "adEe:gh:i:r:Rs:tT:qvx:ln:",longopts,&longindex)) != -1)) { - switch (arg) { - case '?': - default: - fprintf(stderr,"error: unknown option %c\n",optopt); - errflg++; - break; - case 'a': - break; - case 'A': // not implemented - showAddress = true; - break; - case 'c': - cleo = true; - break; - case 'd': - output_dir = optarg; // reference to an argv pointer - break; - case 'R': - break; - case 'e': - exclusions.add(optarg); - break; - case 'g': - break; - case 'r': // not implemented - roots.add(optarg); - if (!includes.IsEmpty()) { - errflg++; - } - break; - case 'i': - includes.add(optarg); - if (!roots.IsEmpty()) { - errflg++; - } - break; - case 'h': - break; - case 's': - stackDepth = atoi(optarg); - if (stackDepth < 2) { - stackDepth = 2; - } - break; - case 'x': - // --start - collect_start = atoi(optarg); - break; - case 'n': - // --end - collect_end = atoi(optarg); - break; - case 'l': - // --last - collect_last = true; - break; - case 'q': - break; - case 'v': - quiet = !quiet; - break; - case 't': - showThreads = true; - break; - case 'T': - showThreads = true; - onlyThread = atoi(optarg); - break; - } - } - if (errflg || ((argc - optind) < 2)) { - usageError(); - } - progFile = argv[optind++]; - logFileIndex = optind; - numLogFiles = argc - optind; - if (!quiet) - fprintf(stderr,"numlogfiles = %d\n",numLogFiles); -} - -static void* mapFile(int fd, u_int flags, off_t* sz) -{ - struct stat sb; - if (fstat(fd, &sb) < 0) { - perror("fstat"); - exit(-1); - } - void* base = mmap(0, (int)sb.st_size, flags, MAP_PRIVATE, fd, 0); - if (!base) { - perror("mmap"); - exit(-1); - } - *sz = sb.st_size; - return base; -} - -void leaky::LoadMap() -{ - malloc_map_entry mme; - char name[1000]; - - if (!loadMap) { - // all files use the same map - int fd = ::open(M_MAPFILE, O_RDONLY); - if (fd < 0) { - perror("open: " M_MAPFILE); - exit(-1); - } - for (;;) { - int nb = read(fd, &mme, sizeof(mme)); - if (nb != sizeof(mme)) break; - nb = read(fd, name, mme.nameLen); - if (nb != (int)mme.nameLen) break; - name[mme.nameLen] = 0; - if (!quiet) { - fprintf(stderr,"%s @ %lx\n", name, mme.address); - } - - LoadMapEntry* lme = new LoadMapEntry; - lme->address = mme.address; - lme->name = strdup(name); - lme->next = loadMap; - loadMap = lme; - } - close(fd); - } -} - -void leaky::open(char *logFile) -{ - int threadArray[100]; // should auto-expand - int last_thread = -1; - int numThreads = 0; - int section = -1; - bool collecting = false; - - LoadMap(); - - setupSymbols(progFile); - - // open up the log file - if (mappedLogFile) - ::close(mappedLogFile); - - mappedLogFile = ::open(logFile, O_RDONLY); - if (mappedLogFile < 0) { - perror("open"); - exit(-1); - } - off_t size; - firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size); - lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size); - - if (!collect_last || collect_start < 0) { - collecting = true; - } - - // First, restrict it to the capture sections specified (all, last, start/end) - // This loop walks through all the call stacks we recorded - for (malloc_log_entry* lep=firstLogEntry; - lep < lastLogEntry; - lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) { - - if (lep->flags & JP_FIRST_AFTER_PAUSE) { - section++; - if (collect_last) { - firstLogEntry = lep; - numThreads = 0; - collecting = true; - } - if (collect_start == section) { - collecting = true; - firstLogEntry = lep; - } - if (collect_end == section) { - collecting = false; - lastLogEntry = lep; - } - if (!quiet) - fprintf(stderr,"New section %d: first=%p, last=%p, collecting=%d\n", - section,(void*)firstLogEntry,(void*)lastLogEntry,collecting); - } - - // Capture thread info at the same time - - // Find all the threads captured - - // pthread/linux docs say the signal can be delivered to any thread in - // the process. In practice, it appears in Linux that it's always - // delivered to the thread that called setitimer(), and each thread can - // have a separate itimer. There's a support library for gprof that - // overlays pthread_create() to set timers in any threads you spawn. - if (showThreads && collecting) { - if (lep->thread != last_thread) - { - int i; - for (i=0; i<numThreads; i++) - { - if (lep->thread == threadArray[i]) - break; - } - if (i == numThreads && - i < (int) (sizeof(threadArray)/sizeof(threadArray[0]))) - { - threadArray[i] = lep->thread; - numThreads++; - if (!quiet) - fprintf(stderr,"new thread %d\n",lep->thread); - } - } - } - } - if (!quiet) - fprintf(stderr,"Done collecting: sections %d: first=%p, last=%p, numThreads=%d\n", - section,(void*)firstLogEntry,(void*)lastLogEntry,numThreads); - - if (!cleo) { - fprintf(outputfd,"<html><head><title>Jprof Profile Report</title></head><body>\n"); - fprintf(outputfd,"<h1><center>Jprof Profile Report</center></h1>\n"); - } - - if (showThreads) - { - fprintf(stderr,"Num threads %d\n",numThreads); - - if (!cleo) { - fprintf(outputfd,"<hr>Threads:<p><pre>\n"); - for (int i=0; i<numThreads; i++) - { - fprintf(outputfd," <a href=\"#thread_%d\">%d</a> ", - threadArray[i],threadArray[i]); - if ((i+1)%10 == 0) - fprintf(outputfd,"<br>\n"); - } - fprintf(outputfd,"</pre>"); - } - - for (int i=0; i<numThreads; i++) - { - if (!onlyThread || onlyThread == threadArray[i]) - analyze(threadArray[i]); - } - } - else - { - analyze(0); - } - - if (!cleo) - fprintf(outputfd,"</pre></body></html>\n"); -} - -//---------------------------------------------------------------------- - - -static int symbolOrder(void const* a, void const* b) -{ - Symbol const** ap = (Symbol const **)a; - Symbol const** bp = (Symbol const **)b; - return (*ap)->address == (*bp)->address ? 0 : - ((*ap)->address > (*bp)->address ? 1 : -1); -} - -void leaky::ReadSharedLibrarySymbols() -{ - LoadMapEntry* lme = loadMap; - while (nullptr != lme) { - ReadSymbols(lme->name, lme->address); - lme = lme->next; - } -} - -void leaky::setupSymbols(const char *fileName) -{ - if (usefulSymbols == 0) { - // only read once! - - // Read in symbols from the program - ReadSymbols(fileName, 0); - - // Read in symbols from the .so's - ReadSharedLibrarySymbols(); - - if (!quiet) { - fprintf(stderr,"A total of %d symbols were loaded\n", usefulSymbols); - } - - // Now sort them - qsort(externalSymbols, usefulSymbols, sizeof(Symbol *), symbolOrder); - lowestSymbolAddr = externalSymbols[0]->address; - highestSymbolAddr = externalSymbols[usefulSymbols-1]->address; - } -} - -// Binary search the table, looking for a symbol that covers this -// address. -int leaky::findSymbolIndex(u_long addr) -{ - u_int base = 0; - u_int limit = usefulSymbols - 1; - Symbol** end = &externalSymbols[limit]; - while (base <= limit) { - u_int midPoint = (base + limit)>>1; - Symbol** sp = &externalSymbols[midPoint]; - if (addr < (*sp)->address) { - if (midPoint == 0) { - return -1; - } - limit = midPoint - 1; - } else { - if (sp+1 < end) { - if (addr < (*(sp+1))->address) { - return midPoint; - } - } else { - return midPoint; - } - base = midPoint + 1; - } - } - return -1; -} - -Symbol* leaky::findSymbol(u_long addr) -{ - int idx = findSymbolIndex(addr); - - if(idx<0) { - return nullptr; - } else { - return externalSymbols[idx]; - } -} - -//---------------------------------------------------------------------- - -bool leaky::excluded(malloc_log_entry* lep) -{ - if (exclusions.IsEmpty()) { - return false; - } - - char** pcp = &lep->pcs[0]; - u_int n = lep->numpcs; - for (u_int i = 0; i < n; i++, pcp++) { - Symbol* sp = findSymbol((u_long) *pcp); - if (sp && exclusions.contains(sp->name)) { - return true; - } - } - return false; -} - -bool leaky::included(malloc_log_entry* lep) -{ - if (includes.IsEmpty()) { - return true; - } - - char** pcp = &lep->pcs[0]; - u_int n = lep->numpcs; - for (u_int i = 0; i < n; i++, pcp++) { - Symbol* sp = findSymbol((u_long) *pcp); - if (sp && includes.contains(sp->name)) { - return true; - } - } - return false; -} - -//---------------------------------------------------------------------- - -void leaky::displayStackTrace(FILE* out, malloc_log_entry* lep) -{ - char** pcp = &lep->pcs[0]; - u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; - for (u_int i = 0; i < n; i++, pcp++) { - u_long addr = (u_long) *pcp; - Symbol* sp = findSymbol(addr); - if (sp) { - fputs(sp->name, out); - if (showAddress) { - fprintf(out, "[%p]", (char*)addr); - } - } - else { - fprintf(out, "<%p>", (char*)addr); - } - fputc(' ', out); - } - fputc('\n', out); -} - -void leaky::dumpEntryToLog(malloc_log_entry* lep) -{ - printf("%ld\t", lep->delTime); - printf(" --> "); - displayStackTrace(outputfd, lep); -} - -void leaky::generateReportHTML(FILE *fp, int *countArray, int count, int thread) -{ - fprintf(fp,"<center>"); - if (showThreads) - { - fprintf(fp,"<hr><A NAME=thread_%d><b>Thread: %d</b></A><p>", - thread,thread); - } - fprintf(fp,"<A href=#flat_%d>flat</A><b> | </b><A href=#hier_%d>hierarchical</A>", - thread,thread); - fprintf(fp,"</center><P><P><P>\n"); - - int totalTimerHits = count; - int *rankingTable = new int[usefulSymbols]; - - for(int cnt=usefulSymbols; --cnt>=0; rankingTable[cnt]=cnt); - - // Drat. I would use ::qsort() but I would need a global variable and my - // intro-pascal professor threatened to flunk anyone who used globals. - // She damaged me for life :-) (That was 1986. See how much influence - // she had. I don't remember her name but I always feel guilty about globals) - - // Shell Sort. 581130733 is the max 31 bit value of h = 3h+1 - int mx, i, h; - for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) { - if(h<mx) { - for(i = h-1; i<usefulSymbols; i++) { - int j, tmp=rankingTable[i], val = countArray[tmp]; - for(j = i; (j>=h) && (countArray[rankingTable[j-h]]<val); j-=h) { - rankingTable[j] = rankingTable[j-h]; - } - rankingTable[j] = tmp; - } - } - } - - // Ok, We are sorted now. Let's go through the table until we get to - // functions that were never called. Right now we don't do much inside - // this loop. Later we can get callers and callees into it like gprof - // does - fprintf(fp, - "<h2><A NAME=hier_%d></A><center><a href=\"http://dxr.mozilla.org/mozilla-central/source/tools/jprof/README.html#hier\">Hierarchical Profile</a></center></h2><hr>\n", - thread); - fprintf(fp, "<pre>\n"); - fprintf(fp, "%6s %6s %4s %s\n", - "index", "Count", "Hits", "Function Name"); - - for(i=0; i<usefulSymbols && countArray[rankingTable[i]]>0; i++) { - Symbol **sp=&externalSymbols[rankingTable[i]]; - - (*sp)->cntP.printReport(fp, this, rankingTable[i], totalTimerHits); - - char *symname = htmlify((*sp)->name); - fprintf(fp, "%6d %6d (%3.1f%%)%s <a name=%d>%8d (%3.1f%%)</a>%s <b>%s</b>\n", - rankingTable[i], - (*sp)->timerHit, ((*sp)->timerHit*1000/totalTimerHits)/10.0, - ((*sp)->timerHit*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ", - rankingTable[i], countArray[rankingTable[i]], - (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0, - (countArray[rankingTable[i]]*1000/totalTimerHits)/10.0 >= 10.0 ? "" : " ", - symname); - delete [] symname; - - (*sp)->cntC.printReport(fp, this, rankingTable[i], totalTimerHits); - - fprintf(fp, "<hr>\n"); - } - fprintf(fp,"</pre>\n"); - - // OK, Now we want to print the flat profile. To do this we resort on - // the hit count. - - // Cut-N-Paste Shell sort from above. The Ranking Table has already been - // populated, so we do not have to reinitialize it. - for(mx=usefulSymbols/9, h=581130733; h>0; h/=3) { - if(h<mx) { - for(i = h-1; i<usefulSymbols; i++) { - int j, tmp=rankingTable[i], val = externalSymbols[tmp]->timerHit; - for(j = i; - (j>=h) && (externalSymbols[rankingTable[j-h]]->timerHit<val); j-=h) { - rankingTable[j] = rankingTable[j-h]; - } - rankingTable[j] = tmp; - } - } - } - - // Pre-count up total counter hits, to get a percentage. - // I wanted the total before walking the list, if this - // double-pass over externalSymbols gets slow we can - // do single-pass and print this out after the loop finishes. - totalTimerHits = 0; - for(i=0; - i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) { - Symbol **sp=&externalSymbols[rankingTable[i]]; - totalTimerHits += (*sp)->timerHit; - } - if (totalTimerHits == 0) - totalTimerHits = 1; - - if (totalTimerHits != count) - fprintf(stderr,"Hit count mismatch: count=%d; totalTimerHits=%d", - count,totalTimerHits); - - fprintf(fp,"<h2><A NAME=flat_%d></A><center><a href=\"http://dxr.mozilla.org/mozilla-central/source/tools/jprof/README.html#flat\">Flat Profile</a></center></h2><br>\n", - thread); - fprintf(fp, "<pre>\n"); - - fprintf(fp, "Total hit count: %d\n", totalTimerHits); - fprintf(fp, "Count %%Total Function Name\n"); - // Now loop for as long as we have timer hits - for(i=0; - i<usefulSymbols && externalSymbols[rankingTable[i]]->timerHit>0; i++) { - - Symbol **sp=&externalSymbols[rankingTable[i]]; - - char *symname = htmlify((*sp)->name); - fprintf(fp, "<a href=\"#%d\">%3d %-2.1f %s</a>\n", - rankingTable[i], (*sp)->timerHit, - ((float)(*sp)->timerHit/(float)totalTimerHits)*100.0, symname); - delete [] symname; - } -} - -void leaky::analyze(int thread) -{ - int *countArray = new int[usefulSymbols]; - int *flagArray = new int[usefulSymbols]; - - //Zero our function call counter - memset(countArray, 0, sizeof(countArray[0])*usefulSymbols); - - // reset hit counts - for(int i=0; i<usefulSymbols; i++) { - externalSymbols[i]->timerHit = 0; - externalSymbols[i]->regClear(); - } - - // The flag array is used to prevent counting symbols multiple times - // if functions are called recursively. In order to keep from having - // to zero it on each pass through the loop, we mark it with the value - // of stacks on each trip through the loop. This means we can determine - // if we have seen this symbol for this stack trace w/o having to reset - // from the prior stacktrace. - memset(flagArray, -1, sizeof(flagArray[0])*usefulSymbols); - - if (cleo) - fprintf(outputfd,"m-Start\n"); - - // This loop walks through all the call stacks we recorded - // --last, --start and --end can restrict it, as can excludes/includes - stacks = 0; - for(malloc_log_entry* lep=firstLogEntry; - lep < lastLogEntry; - lep = reinterpret_cast<malloc_log_entry*>(&lep->pcs[lep->numpcs])) { - - if ((thread != 0 && lep->thread != thread) || - excluded(lep) || !included(lep)) - { - continue; - } - - ++stacks; // How many stack frames did we collect - - u_int n = (lep->numpcs < stackDepth) ? lep->numpcs : stackDepth; - char** pcp = &lep->pcs[n-1]; - int idx=-1, parrentIdx=-1; // Init idx incase n==0 - if (cleo) { - // This loop walks through every symbol in the call stack. By walking it - // backwards we know who called the function when we get there. - char type = 's'; - for (int i=n-1; i>=0; --i, --pcp) { - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - - if(idx>=0) { - // Skip over bogus __restore_rt frames that realtime profiling - // can introduce. - if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) { - --pcp; - --i; - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - if (idx < 0) { - continue; - } - } - Symbol **sp=&externalSymbols[idx]; - char *symname = htmlify((*sp)->name); - fprintf(outputfd,"%c-%s\n",type,symname); - delete [] symname; - } - // else can't find symbol - ignore - type = 'c'; - } - } else { - // This loop walks through every symbol in the call stack. By walking it - // backwards we know who called the function when we get there. - for (int i=n-1; i>=0; --i, --pcp) { - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - - if(idx>=0) { - // Skip over bogus __restore_rt frames that realtime profiling - // can introduce. - if (i > 0 && !strcmp(externalSymbols[idx]->name, "__restore_rt")) { - --pcp; - --i; - idx = findSymbolIndex(reinterpret_cast<u_long>(*pcp)); - if (idx < 0) { - continue; - } - } - - // If we have not seen this symbol before count it and mark it as seen - if(flagArray[idx]!=stacks && ((flagArray[idx]=stacks) || true)) { - ++countArray[idx]; - } - - // We know who we are and we know who our parrent is. Count this - if(parrentIdx>=0) { - externalSymbols[parrentIdx]->regChild(idx); - externalSymbols[idx]->regParrent(parrentIdx); - } - // inside if() so an unknown in the middle of a stack won't break - // the link! - parrentIdx=idx; - } - } - - // idx should be the function that we were in when we received the signal. - if(idx>=0) { - ++externalSymbols[idx]->timerHit; - } - - } - } - if (!cleo) - generateReportHTML(outputfd, countArray, stacks, thread); -} - -void FunctionCount::printReport(FILE *fp, leaky *lk, int parent, int total) -{ - const char *fmt = " <A href=\"#%d\">%8d (%3.1f%%)%s %s</A>%s\n"; - - int nmax, tmax=((~0U)>>1); - - do { - nmax=0; - for(int j=getSize(); --j>=0;) { - int cnt = getCount(j); - if(cnt==tmax) { - int idx = getIndex(j); - char *symname = htmlify(lk->indexToName(idx)); - fprintf(fp, fmt, idx, getCount(j), - getCount(j)*100.0/total, - getCount(j)*100.0/total >= 10.0 ? "" : " ", - symname, - parent == idx ? " (self)" : ""); - delete [] symname; - } else if(cnt<tmax && cnt>nmax) { - nmax=cnt; - } - } - } while((tmax=nmax)>0); -} diff --git a/tools/jprof/leaky.h b/tools/jprof/leaky.h deleted file mode 100644 index 5dafe30b9..000000000 --- a/tools/jprof/leaky.h +++ /dev/null @@ -1,122 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef __leaky_h_ -#define __leaky_h_ - -#include "config.h" -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include "libmalloc.h" -#include "strset.h" -#include "intcnt.h" - -typedef unsigned int u_int; - -struct Symbol; -struct leaky; - -class FunctionCount : public IntCount -{ -public: - void printReport(FILE *fp, leaky *lk, int parent, int total); -}; - -struct Symbol { - char* name; - u_long address; - int timerHit; - FunctionCount cntP, cntC; - - int regChild(int id) {return cntC.countAdd(id, 1);} - int regParrent(int id) {return cntP.countAdd(id, 1);} - void regClear() {cntC.clear(); cntP.clear();} - - Symbol() : timerHit(0) {} - void Init(const char* aName, u_long aAddress) { - name = aName ? strdup(aName) : (char *)""; - address = aAddress; - } -}; - -struct LoadMapEntry { - char* name; // name of .so - u_long address; // base address where it was mapped in - LoadMapEntry* next; -}; - -struct leaky { - leaky(); - ~leaky(); - - void initialize(int argc, char** argv); - void open(char *arg); - - char* applicationName; - int logFileIndex; - int numLogFiles; - char* progFile; - FILE* outputfd; - - bool quiet; - bool showAddress; - bool showThreads; - bool cleo; - u_int stackDepth; - int onlyThread; - char* output_dir; - - int mappedLogFile; - malloc_log_entry* firstLogEntry; - malloc_log_entry* lastLogEntry; - - int stacks; - - int sfd; - Symbol** externalSymbols; - Symbol** lastSymbol; - int usefulSymbols; - int numExternalSymbols; - StrSet exclusions; - u_long lowestSymbolAddr; - u_long highestSymbolAddr; - - LoadMapEntry* loadMap; - - bool collect_last; - int collect_start; - int collect_end; - - StrSet roots; - StrSet includes; - - void usageError(); - - void LoadMap(); - - void analyze(int thread); - - void dumpEntryToLog(malloc_log_entry* lep); - - void insertAddress(u_long address, malloc_log_entry* lep); - void removeAddress(u_long address, malloc_log_entry* lep); - - void displayStackTrace(FILE* out, malloc_log_entry* lep); - - Symbol ** ExtendSymbols(int num); - void ReadSymbols(const char* fileName, u_long aBaseAddress); - void ReadSharedLibrarySymbols(); - void setupSymbols(const char* fileName); - Symbol* findSymbol(u_long address); - bool excluded(malloc_log_entry* lep); - bool included(malloc_log_entry* lep); - const char* indexToName(int idx) {return externalSymbols[idx]->name;} - - private: - void generateReportHTML(FILE *fp, int *countArray, int count, int thread); - int findSymbolIndex(u_long address); -}; - -#endif /* __leaky_h_ */ diff --git a/tools/jprof/moz.build b/tools/jprof/moz.build deleted file mode 100644 index 680161e81..000000000 --- a/tools/jprof/moz.build +++ /dev/null @@ -1,28 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -DIRS += ['stub'] - -Program('jprof') - -SOURCES += [ - 'bfd.cpp', - 'coff.cpp', - 'elf.cpp', - 'intcnt.cpp', - 'leaky.cpp', - 'strset.cpp', -] - -LOCAL_INCLUDES += [ - 'stub', -] - -OS_LIBS += [ - 'dl', - 'bfd', - 'iberty', -] diff --git a/tools/jprof/split-profile.py b/tools/jprof/split-profile.py deleted file mode 100755 index 89454d3eb..000000000 --- a/tools/jprof/split-profile.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/python -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# This program splits up a jprof profile into multiple files based on a -# list of functions in a text file. First, a complete profile is -# generated. Then, for each line in the text file, a profile is -# generated containing only stacks that go through that line, and also -# excluding all stacks in earlier lines in the text file. This means -# that the text file, from start to end, is splitting out pieces of the -# profile in their own file. Finally, a final profile containing the -# remainder is produced. - -# The program takes four arguments: -# (1) The path to jprof. -# (2) The path to the text file describing the splits. The output -# will be placed in the same directory as this file. -# (3) The program that was profiled. -# (4) The jprof-log file generated by the profile, to be split up. -# (Really, all arguments from (3) and later are passed through to -# jprof, so additional arguments could be provided if you want to pass -# additional arguments to jprof.) - -# In slightly more detail: -# -# This script uses jprof's includes (-i) and excludes (-e) options to -# split profiles into segments. It takes as input a single text file, -# and from that text file creates a series of jprof profiles in the -# directory the text file is in. -# -# The input file format looks like the following: -# -# poll g_main_poll -# GetRuleCascade CSSRuleProcessor::GetRuleCascade(nsPresContext *, nsIAtom *) -# RuleProcessorData RuleProcessorData::RuleProcessorData(nsPresContext *, nsIContent *, nsRuleWalker *, nsCompatibility *) -# -# From this input file, the script will construct a profile called -# jprof-0.html that contains the whole profile, a profile called -# jprof-1-poll.html that includes only stacks with g_main_poll, a -# profile called jprof-2-GetRuleCascade.html that includes only stacks -# that have GetRuleCascade and do not have g_main_poll, a profile called -# jprof-3-RuleProcessorData.html that includes only stacks that have the -# RuleProcessorData constructor and do not have GetRuleCascade or -# g_main_poll, and a profile called jprof-4.html that includes only -# stacks that do not have any of the three functions in them. -# -# This means that all of the segments of the profile, except -# jprof-0.html, are mutually exclusive. Thus clever ordering of the -# functions in the input file can lead to a logical splitting of the -# profile into segments. - -import sys -import subprocess -import os.path - -if len(sys.argv) < 5: - sys.stderr.write("Expected arguments: <jprof> <split-file> <program> <jprof-log>\n") - sys.exit(1) - -jprof = sys.argv[1] -splitfile = sys.argv[2] -passthrough = sys.argv[3:] - -for f in [jprof, splitfile]: - if not os.path.isfile(f): - sys.stderr.write("could not find file: {0}\n".format(f)) - sys.exit(1) - -def read_splits(splitfile): - """ - Read splitfile (each line of which contains a name, a space, and - then a function name to split on), and return a list of pairs - representing exactly that. (Note that the name cannot contain - spaces, but the function name can, and often does.) - """ - def line_to_split(line): - line = line.strip("\r\n") - idx = line.index(" ") - return (line[0:idx], line[idx+1:]) - - io = open(splitfile, "r") - result = [line_to_split(line) for line in io] - io.close() - return result - -splits = read_splits(splitfile) - -def generate_profile(options, destfile): - """ - Run jprof to generate one split of the profile. - """ - args = [jprof] + options + passthrough - print "Generating {0}".format(destfile) - destio = open(destfile, "w") - # jprof expects the "jprof-map" file to be in its current working directory - cwd = None - for option in passthrough: - if option.find("jprof-log"): - cwd = os.path.dirname(option) - if cwd is None: - raise StandardError("no jprof-log option given") - process = subprocess.Popen(args, stdout=destio, cwd=cwd) - process.wait() - destio.close() - if process.returncode != 0: - os.remove(destfile) - sys.stderr.write("Error {0} from command:\n {1}\n".format(process.returncode, " ".join(args))) - sys.exit(process.returncode) - -def output_filename(number, splitname): - """ - Return the filename (absolute path) we should use to output the - profile segment with the given number and splitname. Splitname - should be None for the complete profile and the remainder. - """ - def pad_count(i): - result = str(i) - # 0-pad to the same length - result = "0" * (len(str(len(splits) + 1)) - len(result)) + result - return result - - name = pad_count(number) - if splitname is not None: - name += "-" + splitname - - return os.path.join(os.path.dirname(splitfile), - "jprof-{0}.html".format(name)) - -# generate the complete profile -generate_profile([], output_filename(0, None)) - -# generate the listed splits -count = 1 -excludes = [] -for (splitname, splitfunction) in splits: - generate_profile(excludes + ["-i" + splitfunction], - output_filename(count, splitname)) - excludes += ["-e" + splitfunction] - count = count + 1 - -# generate the remainder after the splits -generate_profile(excludes, output_filename(count, None)) diff --git a/tools/jprof/strset.cpp b/tools/jprof/strset.cpp deleted file mode 100644 index 623ad3f90..000000000 --- a/tools/jprof/strset.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "strset.h" -#include <malloc.h> -#include <string.h> - -StrSet::StrSet() -{ - strings = 0; - numstrings = 0; -} - -void StrSet::add(const char* s) -{ - if (strings) { - strings = (char**) realloc(strings, (numstrings + 1) * sizeof(char*)); - } else { - strings = (char**) malloc(sizeof(char*)); - } - strings[numstrings] = strdup(s); - numstrings++; -} - -int StrSet::contains(const char* s) -{ - char** sp = strings; - int i = numstrings; - - while (--i >= 0) { - char *ss = *sp++; - if (ss[0] == s[0]) { - if (strcmp(ss, s) == 0) { - return 1; - } - } - } - return 0; -} diff --git a/tools/jprof/strset.h b/tools/jprof/strset.h deleted file mode 100644 index 681ed22a2..000000000 --- a/tools/jprof/strset.h +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef __strset_h_ -#define __strset_h_ - -struct StrSet { - StrSet(); - - void add(const char* string); - int contains(const char* string); - bool IsEmpty() const { return 0 == numstrings; } - - char** strings; - int numstrings; -}; - -#endif /* __strset_h_ */ diff --git a/tools/jprof/stub/Makefile.in b/tools/jprof/stub/Makefile.in deleted file mode 100644 index 8e6b6b8f8..000000000 --- a/tools/jprof/stub/Makefile.in +++ /dev/null @@ -1,8 +0,0 @@ -#! gmake -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# override optimization -MOZ_OPTIMIZE_FLAGS = -fno-omit-frame-pointer diff --git a/tools/jprof/stub/config.h b/tools/jprof/stub/config.h deleted file mode 100644 index d43ffcaa6..000000000 --- a/tools/jprof/stub/config.h +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef config_h___ -#define config_h___ - -#define MAX_STACK_CRAWL 500 -#define M_LOGFILE "jprof-log" -#define M_MAPFILE "jprof-map" - -#if defined(linux) || defined(NTO) -#define USE_BFD -#undef NEED_WRAPPERS - -#endif /* linux */ - -#endif /* config_h___ */ diff --git a/tools/jprof/stub/jprof.h b/tools/jprof/stub/jprof.h deleted file mode 100644 index 5ee99279c..000000000 --- a/tools/jprof/stub/jprof.h +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jprof_h___ -#define jprof_h___ -#include "nscore.h" - -#ifdef _IMPL_JPPROF_API -#define JPROF_API(type) NS_EXPORT_(type) -#else -#define JPROF_API(type) NS_IMPORT_(type) -#endif - -JPROF_API(void) setupProfilingStuff(void); - -#endif /* jprof_h___ */ diff --git a/tools/jprof/stub/libmalloc.cpp b/tools/jprof/stub/libmalloc.cpp deleted file mode 100644 index 2da2a37b3..000000000 --- a/tools/jprof/stub/libmalloc.cpp +++ /dev/null @@ -1,790 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -// vim:cindent:sw=4:et:ts=8: -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// The linux glibc hides part of sigaction if _POSIX_SOURCE is defined -#if defined(linux) -#undef _POSIX_SOURCE -#undef _SVID_SOURCE -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#endif - -#include <errno.h> -#if defined(linux) -#include <linux/rtc.h> -#include <pthread.h> -#endif -#include <unistd.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <ucontext.h> -#include <execinfo.h> - -#include "libmalloc.h" -#include "jprof.h" -#include <string.h> -#include <errno.h> -#include <dlfcn.h> - -// Must define before including jprof.h -void *moz_xmalloc(size_t size) -{ - return malloc(size); -} - -void moz_xfree(void *mem) -{ - free(mem); -} - -#ifdef NTO -#include <sys/link.h> -extern r_debug _r_debug; -#else -#include <link.h> -#endif - -#define USE_GLIBC_BACKTRACE 1 -// To debug, use #define JPROF_STATIC -#define JPROF_STATIC static - -static int gLogFD = -1; -static pthread_t main_thread; - -static bool gIsSlave = false; -static int gFilenamePID; - -static void startSignalCounter(unsigned long millisec); -static int enableRTCSignals(bool enable); - - -//---------------------------------------------------------------------- -// replace use of atexit() - -static void DumpAddressMap(); - -struct JprofShutdown { - JprofShutdown() {} - ~JprofShutdown() { - DumpAddressMap(); - } -}; - -static void RegisterJprofShutdown() { - // This instanciates the dummy class above, and will trigger the class - // destructor when libxul is unloaded. This is equivalent to atexit(), - // but gracefully handles dlclose(). - static JprofShutdown t; -} - -#if defined(i386) || defined(_i386) || defined(__x86_64__) -JPROF_STATIC void CrawlStack(malloc_log_entry* me, - void* stack_top, void* top_instr_ptr) -{ -#if USE_GLIBC_BACKTRACE - // This probably works on more than x86! But we need a way to get the - // top instruction pointer, which is kindof arch-specific - void *array[500]; - int cnt, i; - u_long numpcs = 0; - - // This is from glibc. A more generic version might use - // libunwind and/or CaptureStackBackTrace() on Windows - cnt = backtrace(&array[0],sizeof(array)/sizeof(array[0])); - - // StackHook->JprofLog->CrawlStack - // Then we have sigaction, which replaced top_instr_ptr - array[3] = top_instr_ptr; - for (i = 3; i < cnt; i++) - { - me->pcs[numpcs++] = (char *) array[i]; - } - me->numpcs = numpcs; - -#else - // original code - this breaks on many platforms - void **bp; -#if defined(__i386) - __asm__( "movl %%ebp, %0" : "=g"(bp)); -#elif defined(__x86_64__) - __asm__( "movq %%rbp, %0" : "=g"(bp)); -#else - // It would be nice if this worked uniformly, but at least on i386 and - // x86_64, it stopped working with gcc 4.1, because it points to the - // end of the saved registers instead of the start. - bp = __builtin_frame_address(0); -#endif - u_long numpcs = 0; - bool tracing = false; - - me->pcs[numpcs++] = (char*) top_instr_ptr; - - while (numpcs < MAX_STACK_CRAWL) { - void** nextbp = (void**) *bp++; - void* pc = *bp; - if (nextbp < bp) { - break; - } - if (tracing) { - // Skip the signal handling. - me->pcs[numpcs++] = (char*) pc; - } - else if (pc == top_instr_ptr) { - tracing = true; - } - bp = nextbp; - } - me->numpcs = numpcs; -#endif -} -#endif - -//---------------------------------------------------------------------- - -static int rtcHz; -static int rtcFD = -1; -static bool circular = false; - -#if defined(linux) || defined(NTO) -static void DumpAddressMap() -{ - // Turn off the timer so we don't get interrupts during shutdown -#if defined(linux) - if (rtcHz) { - enableRTCSignals(false); - } else -#endif - { - startSignalCounter(0); - } - - char filename[2048]; - if (gIsSlave) - snprintf(filename, sizeof(filename), "%s-%d", M_MAPFILE, gFilenamePID); - else - snprintf(filename, sizeof(filename), "%s", M_MAPFILE); - - int mfd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666); - if (mfd >= 0) { - malloc_map_entry mme; - link_map* map = _r_debug.r_map; - while (nullptr != map) { - if (map->l_name && *map->l_name) { - mme.nameLen = strlen(map->l_name); - mme.address = map->l_addr; - write(mfd, &mme, sizeof(mme)); - write(mfd, map->l_name, mme.nameLen); -#if 0 - write(1, map->l_name, mme.nameLen); - write(1, "\n", 1); -#endif - } - map = map->l_next; - } - close(mfd); - } -} -#endif - -static bool was_paused = true; - -JPROF_STATIC void JprofBufferDump(); -JPROF_STATIC void JprofBufferClear(); - -static void ClearProfilingHook(int signum) -{ - if (circular) { - JprofBufferClear(); - puts("Jprof: cleared circular buffer."); - } -} - -static void EndProfilingHook(int signum) -{ - if (circular) - JprofBufferDump(); - - DumpAddressMap(); - was_paused = true; - puts("Jprof: profiling paused."); -} - - - -//---------------------------------------------------------------------- -// proper usage would be a template, including the function to find the -// size of an entry, or include a size header explicitly to each entry. -#if defined(linux) -#define DUMB_LOCK() pthread_mutex_lock(&mutex); -#define DUMB_UNLOCK() pthread_mutex_unlock(&mutex); -#else -#define DUMB_LOCK() FIXME() -#define DUMB_UNLOCK() FIXME() -#endif - - -class DumbCircularBuffer -{ -public: - DumbCircularBuffer(size_t init_buffer_size) { - used = 0; - buffer_size = init_buffer_size; - buffer = (unsigned char *) malloc(buffer_size); - head = tail = buffer; - -#if defined(linux) - pthread_mutexattr_t mAttr; - pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE_NP); - pthread_mutex_init(&mutex, &mAttr); - pthread_mutexattr_destroy(&mAttr); -#endif - } - ~DumbCircularBuffer() { - free(buffer); -#if defined(linux) - pthread_mutex_destroy (&mutex); -#endif - } - - void clear() { - DUMB_LOCK(); - head = tail; - used = 0; - DUMB_UNLOCK(); - } - - bool empty() { - return head == tail; - } - - size_t space_available() { - size_t result; - DUMB_LOCK(); - if (tail > head) - result = buffer_size - (tail-head) - 1; - else - result = head-tail - 1; - DUMB_UNLOCK(); - return result; - } - - void drop(size_t size) { - // assumes correctness! - DUMB_LOCK(); - head += size; - if (head >= &buffer[buffer_size]) - head -= buffer_size; - used--; - DUMB_UNLOCK(); - } - - bool insert(void *data, size_t size) { - // can fail if not enough space in the entire buffer - DUMB_LOCK(); - if (space_available() < size) - return false; - - size_t max_without_wrap = &buffer[buffer_size] - tail; - size_t initial = size > max_without_wrap ? max_without_wrap : size; -#if DEBUG_CIRCULAR - fprintf(stderr,"insert(%d): max_without_wrap %d, size %d, initial %d\n",used,max_without_wrap,size,initial); -#endif - memcpy(tail,data,initial); - tail += initial; - data = ((char *)data)+initial; - size -= initial; - if (size != 0) { -#if DEBUG_CIRCULAR - fprintf(stderr,"wrapping by %d bytes\n",size); -#endif - memcpy(buffer,data,size); - tail = &(((unsigned char *)buffer)[size]); - } - - used++; - DUMB_UNLOCK(); - - return true; - } - - // for external access to the buffer (saving) - void lock() { - DUMB_LOCK(); - } - - void unlock() { - DUMB_UNLOCK(); - } - - // XXX These really shouldn't be public... - unsigned char *head; - unsigned char *tail; - unsigned int used; - unsigned char *buffer; - size_t buffer_size; - -private: - pthread_mutex_t mutex; -}; - -class DumbCircularBuffer *JprofBuffer; - -JPROF_STATIC void -JprofBufferInit(size_t size) -{ - JprofBuffer = new DumbCircularBuffer(size); -} - -JPROF_STATIC void -JprofBufferClear() -{ - fprintf(stderr,"Told to clear JPROF circular buffer\n"); - JprofBuffer->clear(); -} - -JPROF_STATIC size_t -JprofEntrySizeof(malloc_log_entry *me) -{ - return offsetof(malloc_log_entry, pcs) + me->numpcs*sizeof(char*); -} - -JPROF_STATIC void -JprofBufferAppend(malloc_log_entry *me) -{ - size_t size = JprofEntrySizeof(me); - - do { - while (JprofBuffer->space_available() < size && - JprofBuffer->used > 0) { -#if DEBUG_CIRCULAR - fprintf(stderr,"dropping entry: %d in use, %d free, need %d, size_to_free = %d\n", - JprofBuffer->used,JprofBuffer->space_available(),size,JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); -#endif - JprofBuffer->drop(JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); - } - if (JprofBuffer->space_available() < size) - return; - - } while (!JprofBuffer->insert(me,size)); -} - -JPROF_STATIC void -JprofBufferDump() -{ - JprofBuffer->lock(); -#if DEBUG_CIRCULAR - fprintf(stderr,"dumping JP_CIRCULAR buffer, %d of %d bytes\n", - JprofBuffer->tail > JprofBuffer->head ? - JprofBuffer->tail - JprofBuffer->head : - JprofBuffer->buffer_size + JprofBuffer->tail - JprofBuffer->head, - JprofBuffer->buffer_size); -#endif - if (JprofBuffer->tail >= JprofBuffer->head) { - write(gLogFD, JprofBuffer->head, JprofBuffer->tail - JprofBuffer->head); - } else { - write(gLogFD, JprofBuffer->head, &(JprofBuffer->buffer[JprofBuffer->buffer_size]) - JprofBuffer->head); - write(gLogFD, JprofBuffer->buffer, JprofBuffer->tail - JprofBuffer->buffer); - } - JprofBuffer->clear(); - JprofBuffer->unlock(); -} - -//---------------------------------------------------------------------- - -JPROF_STATIC void -JprofLog(u_long aTime, void* stack_top, void* top_instr_ptr) -{ - // Static is simply to make debugging tolerable - static malloc_log_entry me; - - me.delTime = aTime; - me.thread = syscall(SYS_gettid); //gettid(); - if (was_paused) { - me.flags = JP_FIRST_AFTER_PAUSE; - was_paused = 0; - } else { - me.flags = 0; - } - - CrawlStack(&me, stack_top, top_instr_ptr); - -#ifndef NTO - if (circular) { - JprofBufferAppend(&me); - } else { - write(gLogFD, &me, JprofEntrySizeof(&me)); - } -#else - printf("Neutrino is missing the pcs member of malloc_log_entry!! \n"); -#endif -} - -static int realTime; - -/* Lets interrupt at 10 Hz. This is so my log files don't get too large. - * This can be changed to a faster value latter. This timer is not - * programmed to reset, even though it is capable of doing so. This is - * to keep from getting interrupts from inside of the handler. -*/ -static void startSignalCounter(unsigned long millisec) -{ - struct itimerval tvalue; - - tvalue.it_interval.tv_sec = 0; - tvalue.it_interval.tv_usec = 0; - tvalue.it_value.tv_sec = millisec/1000; - tvalue.it_value.tv_usec = (millisec%1000)*1000; - - if (realTime) { - setitimer(ITIMER_REAL, &tvalue, nullptr); - } else { - setitimer(ITIMER_PROF, &tvalue, nullptr); - } -} - -static long timerMilliSec = 50; - -#if defined(linux) -static int setupRTCSignals(int hz, struct sigaction *sap) -{ - /* global */ rtcFD = open("/dev/rtc", O_RDONLY); - if (rtcFD < 0) { - perror("JPROF_RTC setup: open(\"/dev/rtc\", O_RDONLY)"); - return 0; - } - - if (sigaction(SIGIO, sap, nullptr) == -1) { - perror("JPROF_RTC setup: sigaction(SIGIO)"); - return 0; - } - - if (ioctl(rtcFD, RTC_IRQP_SET, hz) == -1) { - perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_IRQP_SET, $JPROF_RTC_HZ)"); - return 0; - } - - if (ioctl(rtcFD, RTC_PIE_ON, 0) == -1) { - perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_PIE_ON)"); - return 0; - } - - if (fcntl(rtcFD, F_SETSIG, 0) == -1) { - perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETSIG, 0)"); - return 0; - } - - if (fcntl(rtcFD, F_SETOWN, getpid()) == -1) { - perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETOWN, getpid())"); - return 0; - } - - return 1; -} - -static int enableRTCSignals(bool enable) -{ - static bool enabled = false; - if (enabled == enable) { - return 0; - } - enabled = enable; - - int flags = fcntl(rtcFD, F_GETFL); - if (flags < 0) { - perror("JPROF_RTC setup: fcntl(/dev/rtc, F_GETFL)"); - return 0; - } - - if (enable) { - flags |= FASYNC; - } else { - flags &= ~FASYNC; - } - - if (fcntl(rtcFD, F_SETFL, flags) == -1) { - if (enable) { - perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags | FASYNC)"); - } else { - perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags & ~FASYNC)"); - } - return 0; - } - - return 1; -} -#endif - -JPROF_STATIC void StackHook( -int signum, -siginfo_t *info, -void *ucontext) -{ - static struct timeval tFirst; - static int first=1; - size_t millisec = 0; - -#if defined(linux) - if (rtcHz && pthread_self() != main_thread) { - // Only collect stack data on the main thread, for now. - return; - } -#endif - - if(first && !(first=0)) { - puts("Jprof: received first signal"); -#if defined(linux) - if (rtcHz) { - enableRTCSignals(true); - } else -#endif - { - gettimeofday(&tFirst, 0); - millisec = 0; - } - } else { -#if defined(linux) - if (rtcHz) { - enableRTCSignals(true); - } else -#endif - { - struct timeval tNow; - gettimeofday(&tNow, 0); - double usec = 1e6*(tNow.tv_sec - tFirst.tv_sec); - usec += (tNow.tv_usec - tFirst.tv_usec); - millisec = static_cast<size_t>(usec*1e-3); - } - } - - gregset_t &gregs = ((ucontext_t*)ucontext)->uc_mcontext.gregs; -#ifdef __x86_64__ - JprofLog(millisec, (void*)gregs[REG_RSP], (void*)gregs[REG_RIP]); -#else - JprofLog(millisec, (void*)gregs[REG_ESP], (void*)gregs[REG_EIP]); -#endif - - if (!rtcHz) - startSignalCounter(timerMilliSec); -} - -NS_EXPORT_(void) setupProfilingStuff(void) -{ - static int gFirstTime = 1; - char filename[2048]; // XXX fix - - if(gFirstTime && !(gFirstTime=0)) { - int startTimer = 1; - int doNotStart = 1; - int firstDelay = 0; - int append = O_TRUNC; - char *tst = getenv("JPROF_FLAGS"); - - /* Options from JPROF_FLAGS environment variable: - * JP_DEFER -> Wait for a SIGPROF (or SIGALRM, if JP_REALTIME - * is set) from userland before starting - * to generate them internally - * JP_START -> Install the signal handler - * JP_PERIOD -> Time between profiler ticks - * JP_FIRST -> Extra delay before starting - * JP_REALTIME -> Take stack traces in intervals of real time - * rather than time used by the process (and the - * system for the process). This is useful for - * finding time spent by the X server. - * JP_APPEND -> Append to jprof-log rather than overwriting it. - * This is somewhat risky since it depends on the - * address map staying constant across multiple runs. - * JP_FILENAME -> base filename to use when saving logs. Note that - * this does not affect the mapfile. - * JP_CIRCULAR -> use a circular buffer of size N, write/clear on SIGUSR1 - * - * JPROF_SLAVE is set if this is not the first process. - */ - - circular = false; - - if(tst) { - if(strstr(tst, "JP_DEFER")) - { - doNotStart = 0; - startTimer = 0; - } - if(strstr(tst, "JP_START")) doNotStart = 0; - if(strstr(tst, "JP_REALTIME")) realTime = 1; - if(strstr(tst, "JP_APPEND")) append = O_APPEND; - - char *delay = strstr(tst,"JP_PERIOD="); - if(delay) { - double tmp = strtod(delay+strlen("JP_PERIOD="), nullptr); - if (tmp>=1e-3) { - timerMilliSec = static_cast<unsigned long>(1000 * tmp); - } else { - fprintf(stderr, - "JP_PERIOD of %g less than 0.001 (1ms), using 1ms\n", - tmp); - timerMilliSec = 1; - } - } - - char *circular_op = strstr(tst,"JP_CIRCULAR="); - if(circular_op) { - size_t size = atol(circular_op+strlen("JP_CIRCULAR=")); - if (size < 1000) { - fprintf(stderr, - "JP_CIRCULAR of %lu less than 1000, using 10000\n", - (unsigned long) size); - size = 10000; - } - JprofBufferInit(size); - fprintf(stderr,"JP_CIRCULAR buffer of %lu bytes\n", (unsigned long) size); - circular = true; - } - - char *first = strstr(tst, "JP_FIRST="); - if(first) { - firstDelay = atol(first+strlen("JP_FIRST=")); - } - - char *rtc = strstr(tst, "JP_RTC_HZ="); - if (rtc) { -#if defined(linux) - rtcHz = atol(rtc+strlen("JP_RTC_HZ=")); - timerMilliSec = 0; /* This makes JP_FIRST work right. */ - realTime = 1; /* It's the _R_TC and all. ;) */ - -#define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) - - if (!IS_POWER_OF_TWO(rtcHz) || rtcHz < 2) { - fprintf(stderr, "JP_RTC_HZ must be power of two and >= 2, " - "but %d was provided; using default of 2048\n", - rtcHz); - rtcHz = 2048; - } -#else - fputs("JP_RTC_HZ found, but RTC profiling only supported on " - "Linux!\n", stderr); - -#endif - } - const char *f = strstr(tst,"JP_FILENAME="); - if (f) - f = f + strlen("JP_FILENAME="); - else - f = M_LOGFILE; - - char *is_slave = getenv("JPROF_SLAVE"); - if (!is_slave) - setenv("JPROF_SLAVE","", 0); - gIsSlave = !!is_slave; - - gFilenamePID = syscall(SYS_gettid); //gettid(); - if (is_slave) - snprintf(filename,sizeof(filename),"%s-%d",f,gFilenamePID); - else - snprintf(filename,sizeof(filename),"%s",f); - - // XXX FIX! inherit current capture state! - } - - if(!doNotStart) { - - if(gLogFD<0) { - gLogFD = open(filename, O_CREAT | O_WRONLY | append, 0666); - if(gLogFD<0) { - fprintf(stderr, "Unable to create " M_LOGFILE); - perror(":"); - } else { - struct sigaction action; - sigset_t mset; - - // Dump out the address map when we terminate - RegisterJprofShutdown(); - - main_thread = pthread_self(); - //fprintf(stderr,"jprof: main_thread = %u\n", - // (unsigned int)main_thread); - - // FIX! probably should block these against each other - // Very unlikely. - sigemptyset(&mset); - action.sa_handler = nullptr; - action.sa_sigaction = StackHook; - action.sa_mask = mset; - action.sa_flags = SA_RESTART | SA_SIGINFO; -#if defined(linux) - if (rtcHz) { - if (!setupRTCSignals(rtcHz, &action)) { - fputs("jprof: Error initializing RTC, NOT " - "profiling\n", stderr); - return; - } - } - - if (!rtcHz || firstDelay != 0) -#endif - { - if (realTime) { - sigaction(SIGALRM, &action, nullptr); - } - } - // enable PROF in all cases to simplify JP_DEFER/pause/restart - sigaction(SIGPROF, &action, nullptr); - - // make it so a SIGUSR1 will stop the profiling - // Note: It currently does not close the logfile. - // This could be configurable (so that it could - // later be reopened). - - struct sigaction stop_action; - stop_action.sa_handler = EndProfilingHook; - stop_action.sa_mask = mset; - stop_action.sa_flags = SA_RESTART; - sigaction(SIGUSR1, &stop_action, nullptr); - - // make it so a SIGUSR2 will clear the circular buffer - - stop_action.sa_handler = ClearProfilingHook; - stop_action.sa_mask = mset; - stop_action.sa_flags = SA_RESTART; - sigaction(SIGUSR2, &stop_action, nullptr); - - printf("Jprof: Initialized signal handler and set " - "timer for %lu %s, %d s " - "initial delay\n", - rtcHz ? rtcHz : timerMilliSec, - rtcHz ? "Hz" : "ms", - firstDelay); - - if(startTimer) { -#if defined(linux) - /* If we have an initial delay we can just use - startSignalCounter to set up a timer to fire the - first stackHook after that delay. When that happens - we'll go and switch to RTC profiling. */ - if (rtcHz && firstDelay == 0) { - puts("Jprof: enabled RTC signals"); - enableRTCSignals(true); - } else -#endif - { - puts("Jprof: started timer"); - startSignalCounter(firstDelay*1000 + timerMilliSec); - } - } - } - } - } - } else { - printf("setupProfilingStuff() called multiple times\n"); - } -} diff --git a/tools/jprof/stub/libmalloc.h b/tools/jprof/stub/libmalloc.h deleted file mode 100644 index 8b29bf889..000000000 --- a/tools/jprof/stub/libmalloc.h +++ /dev/null @@ -1,45 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef libmalloc_h___ -#define libmalloc_h___ - -#include <sys/types.h> -#include <malloc.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#include "config.h" - -typedef unsigned long u_long; - -// For me->flags -#define JP_FIRST_AFTER_PAUSE 1 - -// Format of a jprof log entry. This is what's written out to the -// "jprof-log" file. -// It's called malloc_log_entry because the history of jprof is that -// it's a modified version of tracemalloc. -struct malloc_log_entry { - u_long delTime; - u_long numpcs; - unsigned int flags; - int thread; - char* pcs[MAX_STACK_CRAWL]; -}; - -// Format of a malloc map entry; after this struct is nameLen+1 bytes of -// name data. -struct malloc_map_entry { - u_long nameLen; - u_long address; // base address -}; - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* libmalloc_h___ */ diff --git a/tools/jprof/stub/moz.build b/tools/jprof/stub/moz.build deleted file mode 100644 index a2f514a54..000000000 --- a/tools/jprof/stub/moz.build +++ /dev/null @@ -1,17 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -EXPORTS += [ - 'jprof.h', -] - -SOURCES += [ - 'libmalloc.cpp', -] - -SharedLibrary('jprof') - -DEFINES['_IMPL_JPROF_API'] = True |