mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 664453: Stop jprof from crashing before a profile is selected on JP_START. r=dbaron DONTBUILD
This commit is contained in:
parent
953d05c62f
commit
cc44944594
@ -41,6 +41,13 @@ 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) {
|
||||
|
@ -41,6 +41,7 @@ class IntCount
|
||||
public:
|
||||
IntCount();
|
||||
~IntCount();
|
||||
void clear();
|
||||
int countAdd(int index, int increment=1);
|
||||
int countGet(int index);
|
||||
int getSize();
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
@ -19,6 +19,7 @@
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Randell Jesup (recent improvements, threads, etc)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -131,6 +132,7 @@ leaky::leaky()
|
||||
showAddress = FALSE;
|
||||
showThreads = FALSE;
|
||||
stackDepth = 100000;
|
||||
onlyThread = 0;
|
||||
|
||||
mappedLogFile = -1;
|
||||
firstLogEntry = lastLogEntry = 0;
|
||||
@ -143,6 +145,10 @@ leaky::leaky()
|
||||
highestSymbolAddr = 0;
|
||||
|
||||
loadMap = NULL;
|
||||
|
||||
collect_last = false;
|
||||
collect_start = -1;
|
||||
collect_end = -1;
|
||||
}
|
||||
|
||||
leaky::~leaky()
|
||||
@ -151,11 +157,28 @@ leaky::~leaky()
|
||||
|
||||
void leaky::usageError()
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-v][-t] [-e exclude] [-i include] [-s stackdepth] prog log\n", (char*) applicationName);
|
||||
fprintf(stderr, "\t-v: verbose\n\t-t: split threads\n");
|
||||
fprintf(stderr, "Usage: %s [-v] [-t] [-e exclude] [-i include] [-s stackdepth] [--last] [--all] [--start n [--end m]] prog log\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--last: only profile the last capture section\n"
|
||||
"\t--start n [--end m]: profile n to m (or end) capture sections\n"
|
||||
);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static struct option longopts[] = {
|
||||
{ "threads", 0, NULL, 't' },
|
||||
{ "only-thread", 1, NULL, 'T' },
|
||||
{ "last", 0, NULL, 'l' },
|
||||
{ "start", 1, NULL, 'x' },
|
||||
{ "end", 1, NULL, 'n' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
void leaky::initialize(int argc, char** argv)
|
||||
{
|
||||
applicationName = argv[0];
|
||||
@ -168,10 +191,14 @@ void leaky::initialize(int argc, char** argv)
|
||||
|
||||
int arg;
|
||||
int errflg = 0;
|
||||
while ((arg = getopt(argc, argv, "adEe:gh:i:r:Rs:tqvx")) != -1) {
|
||||
int longindex = 0;
|
||||
onlyThread = 0;
|
||||
// XXX tons of cruft here left over from tracemalloc
|
||||
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':
|
||||
@ -209,7 +236,17 @@ void leaky::initialize(int argc, char** argv)
|
||||
}
|
||||
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':
|
||||
@ -218,6 +255,10 @@ void leaky::initialize(int argc, char** argv)
|
||||
case 't':
|
||||
showThreads = TRUE;
|
||||
break;
|
||||
case 'T':
|
||||
showThreads = TRUE;
|
||||
onlyThread = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (errflg || ((argc - optind) < 2)) {
|
||||
@ -260,7 +301,7 @@ void leaky::LoadMap()
|
||||
if (nb != (int)mme.nameLen) break;
|
||||
name[mme.nameLen] = 0;
|
||||
if (!quiet) {
|
||||
printf("%s @ %lx\n", name, mme.address);
|
||||
fprintf(stderr,"%s @ %lx\n", name, mme.address);
|
||||
}
|
||||
|
||||
LoadMapEntry* lme = new LoadMapEntry;
|
||||
@ -276,7 +317,9 @@ void leaky::open()
|
||||
{
|
||||
int threadArray[100]; // should auto-expand
|
||||
int last_thread = -1;
|
||||
int numThreads=0;
|
||||
int numThreads = 0;
|
||||
int section = -1;
|
||||
bool collecting = false;
|
||||
|
||||
LoadMap();
|
||||
|
||||
@ -292,11 +335,37 @@ void leaky::open()
|
||||
firstLogEntry = (malloc_log_entry*) mapFile(mappedLogFile, PROT_READ, &size);
|
||||
lastLogEntry = (malloc_log_entry*)((char*)firstLogEntry + size);
|
||||
|
||||
fprintf(stdout,"<html><head><title>Jprof Profile Report</title></head><body>\n");
|
||||
fprintf(stdout,"<h1><center>Jprof Profile Report</center></h1>\n");
|
||||
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;
|
||||
}
|
||||
fprintf(stderr,"New section %d: first=%x, last=%x, collecting=%d\n",
|
||||
section,firstLogEntry,lastLogEntry,collecting);
|
||||
}
|
||||
|
||||
// Capture thread info at the same time
|
||||
|
||||
if (showThreads)
|
||||
{
|
||||
// Find all the threads captured
|
||||
|
||||
// pthread/linux docs say the signal can be delivered to any thread in
|
||||
@ -304,11 +373,7 @@ void leaky::open()
|
||||
// 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.
|
||||
|
||||
// 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 (showThreads && collecting) {
|
||||
if (lep->thread != last_thread)
|
||||
{
|
||||
int i;
|
||||
@ -322,23 +387,34 @@ void leaky::open()
|
||||
{
|
||||
threadArray[i] = lep->thread;
|
||||
numThreads++;
|
||||
fprintf(stderr,"new thread %d\n",lep->thread);
|
||||
if (!quiet)
|
||||
fprintf(stderr,"new thread %d\n",lep->thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"Done collecting: sections %d: first=%x, last=%x, numThreads=%d\n",
|
||||
section,firstLogEntry,lastLogEntry,numThreads);
|
||||
|
||||
fprintf(stdout,"<html><head><title>Jprof Profile Report</title></head><body>\n");
|
||||
fprintf(stdout,"<h1><center>Jprof Profile Report</center></h1>\n");
|
||||
|
||||
if (showThreads)
|
||||
{
|
||||
fprintf(stderr,"Num threads %d\n",numThreads);
|
||||
|
||||
fprintf(stdout,"<hr>Threads:<p><pre>\n");
|
||||
for (int i=0; i<numThreads; i++)
|
||||
{
|
||||
fprintf(stdout," <a href=\"thread_%d\">%d</a><p>\n",
|
||||
fprintf(stdout," <a href=\"#thread_%d\">%d</a> ",
|
||||
threadArray[i],threadArray[i]);
|
||||
}
|
||||
fprintf(stdout,"</pre><hr>");
|
||||
fprintf(stdout,"</pre>");
|
||||
|
||||
for (int i=0; i<numThreads; i++)
|
||||
{
|
||||
analyze(threadArray[i]);
|
||||
if (!onlyThread || onlyThread == threadArray[i])
|
||||
analyze(threadArray[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -380,7 +456,7 @@ void leaky::setupSymbols(const char *fileName)
|
||||
ReadSharedLibrarySymbols();
|
||||
|
||||
if (!quiet) {
|
||||
printf("A total of %d symbols were loaded\n", usefulSymbols);
|
||||
fprintf(stderr,"A total of %d symbols were loaded\n", usefulSymbols);
|
||||
}
|
||||
|
||||
// Now sort them
|
||||
@ -627,6 +703,12 @@ void leaky::analyze(int thread)
|
||||
//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
|
||||
@ -636,6 +718,7 @@ void leaky::analyze(int thread)
|
||||
memset(flagArray, -1, sizeof(flagArray[0])*usefulSymbols);
|
||||
|
||||
// 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;
|
||||
|
@ -63,6 +63,7 @@ struct Symbol {
|
||||
|
||||
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) {
|
||||
@ -92,6 +93,7 @@ struct leaky {
|
||||
int showAddress;
|
||||
int showThreads;
|
||||
u_int stackDepth;
|
||||
int onlyThread;
|
||||
|
||||
int mappedLogFile;
|
||||
malloc_log_entry* firstLogEntry;
|
||||
@ -109,6 +111,10 @@ struct leaky {
|
||||
|
||||
LoadMapEntry* loadMap;
|
||||
|
||||
bool collect_last;
|
||||
int collect_start;
|
||||
int collect_end;
|
||||
|
||||
StrSet roots;
|
||||
StrSet includes;
|
||||
|
||||
|
@ -213,9 +213,12 @@ static void DumpAddressMap()
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool was_paused = true;
|
||||
|
||||
static void EndProfilingHook(int signum)
|
||||
{
|
||||
DumpAddressMap();
|
||||
was_paused = true;
|
||||
puts("Jprof: profiling paused.");
|
||||
}
|
||||
|
||||
@ -224,11 +227,17 @@ static void EndProfilingHook(int signum)
|
||||
JPROF_STATIC void
|
||||
JprofLog(u_long aTime, void* stack_top, void* top_instr_ptr)
|
||||
{
|
||||
// Static is simply to make debugging tollerable
|
||||
// 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);
|
||||
|
||||
@ -497,11 +506,13 @@ NS_EXPORT_(void) setupProfilingStuff(void)
|
||||
|
||||
if (!rtcHz || firstDelay != 0)
|
||||
#endif
|
||||
if (realTime) {
|
||||
sigaction(SIGALRM, &action, NULL);
|
||||
} else {
|
||||
sigaction(SIGPROF, &action, NULL);
|
||||
{
|
||||
if (realTime) {
|
||||
sigaction(SIGALRM, &action, NULL);
|
||||
}
|
||||
}
|
||||
// enable PROF in all cases to simplify JP_DEFER/pause/restart
|
||||
sigaction(SIGPROF, &action, NULL);
|
||||
|
||||
// make it so a SIGUSR1 will stop the profiling
|
||||
// Note: It currently does not close the logfile.
|
||||
|
@ -47,11 +47,17 @@ extern "C" {
|
||||
|
||||
typedef unsigned long u_long;
|
||||
|
||||
// Format of a malloc log entry. This is what's written out to the
|
||||
// "malloc-log" file.
|
||||
// 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];
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user