mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-06 14:30:38 +08:00
* ssp.c (version): New global variable.
(longopts): Ditto. (opts): Ditto. (run_program): Correct cmd_line typos to cmdline. (usage): New function. Standardize usage output. Add ssp.txt to --help output. (print_version): New function. (main): Accommodate getopt.
This commit is contained in:
parent
ee964e2f97
commit
c240a7fcde
@ -1,3 +1,14 @@
|
|||||||
|
2002-06-03 Joshua Daniel Franklin <joshuadfranklin@yahoo.com>
|
||||||
|
|
||||||
|
* ssp.c (version): New global variable.
|
||||||
|
(longopts): Ditto.
|
||||||
|
(opts): Ditto.
|
||||||
|
(run_program): Correct cmd_line typos to cmdline.
|
||||||
|
(usage): New function. Standardize usage output. Add ssp.txt to
|
||||||
|
--help output.
|
||||||
|
(print_version): New function.
|
||||||
|
(main): Accommodate getopt.
|
||||||
|
|
||||||
2002-06-03 Joshua Daniel Franklin <joshuadfranklin@yahoo.com>
|
2002-06-03 Joshua Daniel Franklin <joshuadfranklin@yahoo.com>
|
||||||
|
|
||||||
* umount.cc (version): New global variable.
|
* umount.cc (version): New global variable.
|
||||||
|
@ -20,53 +20,26 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
#ifdef __GNUC__
|
static const char version[] = "$Revision$";
|
||||||
const char *help_text = "\
|
static char *prog_name;
|
||||||
Usage: ssp [options] low_pc high_pc command...\n\
|
|
||||||
\n\
|
static struct option longopts[] =
|
||||||
The SSP is a `single-step profiler' - it uses the debug API to\n\
|
{
|
||||||
single-step your program, noting *everything* your program runs, not\n\
|
{"console-trace", no_argument, NULL, 'c' },
|
||||||
just random places hit by random timer interrupts. You must specify\n\
|
{"disable", no_argument, NULL, 'd' },
|
||||||
the range of EIP values to profile. For example, you could profile\n\
|
{"enable", no_argument, NULL, 'e' },
|
||||||
just a function, or just a line of code, or the whole thing.\n\
|
{"help", no_argument, NULL, 'h' },
|
||||||
Use \"objdump -h\" to find the start of .text and the section following\n\
|
{"dll", no_argument, NULL, 'l' },
|
||||||
it; this is what you want.\n\
|
{"sub-threads", no_argument, NULL, 's' },
|
||||||
\n\
|
{"trace-eip", no_argument, NULL, 't' },
|
||||||
There are many options to ssp. Since step-profiling makes your\n\
|
{"verbose", no_argument, NULL, 'v' },
|
||||||
program run about 1,000 times slower than normal, it's best to\n\
|
{"version", no_argument, NULL, 'V' },
|
||||||
understand all the options so that you can narrow down the parts\n\
|
{NULL, 0, NULL, 0}
|
||||||
of your program you need to single-step.\n\
|
};
|
||||||
\n\
|
|
||||||
-v = verbose messages about debug events.\n\
|
static char opts[] = "cdehlstvV";
|
||||||
\n\
|
|
||||||
-d, -e = disable/enable single-stepping by default. Use\n\
|
|
||||||
OutputDebugString (\"ssp on\") to enable stepping, or \"ssp off\" to\n\
|
|
||||||
disable it. Thus, you can profile a single function call or block.\n\
|
|
||||||
\n\
|
|
||||||
-t = trace every EIP value to a file TRACE.SSP. This gets big *fast*.\n\
|
|
||||||
Use \"addr2line -C -f -s -e foo.exe < trace.ssp > lines.ssp\" and then\n\
|
|
||||||
\"perl cvttrace\" to convert to symbolic traces.\n\
|
|
||||||
\n\
|
|
||||||
-tc = trace every EIP value to the console. *Lots* slower.\n\
|
|
||||||
\n\
|
|
||||||
-s = trace sub-threads too. Dangerous if you have race conditions.\n\
|
|
||||||
\n\
|
|
||||||
-dll = enable dll profiling. A chart of relative DLL usage is\n\
|
|
||||||
produced after the run.\n\
|
|
||||||
\n\
|
|
||||||
Examples:\n\
|
|
||||||
ssp 0x401000 0x403000 hello.exe\n\
|
|
||||||
ssp -v -d -dll 0x401000 0x440000 foo.exe\n\
|
|
||||||
\n\
|
|
||||||
The output is a file \"gmon.out\" that can be read with gprof:\n\
|
|
||||||
gprof -b foo.exe\n\
|
|
||||||
\n\
|
|
||||||
See ssp.txt in the cygwin sources for more information.\n\
|
|
||||||
";
|
|
||||||
#else
|
|
||||||
char *help_text = "Usage: get cygwin!\n";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define KERNEL_ADDR 0x77000000
|
#define KERNEL_ADDR 0x77000000
|
||||||
|
|
||||||
@ -102,7 +75,6 @@ typedef struct {
|
|||||||
int low_pc=0, high_pc=0;
|
int low_pc=0, high_pc=0;
|
||||||
unsigned int last_pc=0, pc, last_sp=0, sp;
|
unsigned int last_pc=0, pc, last_sp=0, sp;
|
||||||
int total_cycles, count;
|
int total_cycles, count;
|
||||||
char *cmd_line;
|
|
||||||
HANDLE hProcess;
|
HANDLE hProcess;
|
||||||
PROCESS_INFORMATION procinfo;
|
PROCESS_INFORMATION procinfo;
|
||||||
STARTUPINFO startup;
|
STARTUPINFO startup;
|
||||||
@ -330,7 +302,7 @@ run_program (char *cmdline)
|
|||||||
memset (&startup, 0, sizeof (startup));
|
memset (&startup, 0, sizeof (startup));
|
||||||
startup.cb = sizeof (startup);
|
startup.cb = sizeof (startup);
|
||||||
|
|
||||||
if (!CreateProcess (0, cmd_line, 0, 0, 0,
|
if (!CreateProcess (0, cmdline, 0, 0, 0,
|
||||||
CREATE_NEW_PROCESS_GROUP
|
CREATE_NEW_PROCESS_GROUP
|
||||||
| CREATE_SUSPENDED
|
| CREATE_SUSPENDED
|
||||||
| DEBUG_PROCESS
|
| DEBUG_PROCESS
|
||||||
@ -355,7 +327,7 @@ run_program (char *cmdline)
|
|||||||
dll_info[0].base_address = 0;
|
dll_info[0].base_address = 0;
|
||||||
dll_info[0].pcount = 0;
|
dll_info[0].pcount = 0;
|
||||||
dll_info[0].scount = 0;
|
dll_info[0].scount = 0;
|
||||||
dll_info[0].name = cmd_line;
|
dll_info[0].name = cmdline;
|
||||||
num_dlls = 1;
|
num_dlls = 1;
|
||||||
|
|
||||||
SetThreadPriority (procinfo.hThread, THREAD_PRIORITY_IDLE);
|
SetThreadPriority (procinfo.hThread, THREAD_PRIORITY_IDLE);
|
||||||
@ -671,76 +643,250 @@ run_program (char *cmdline)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WHITE(s) while (isspace (*s)) s++
|
static void
|
||||||
|
usage (FILE * stream)
|
||||||
|
{
|
||||||
|
fprintf (stream , ""
|
||||||
|
"Usage: %s [options] low_pc high_pc command...\n"
|
||||||
|
" -c, --console-trace trace every EIP value to the console. *Lots* slower.\n"
|
||||||
|
" -d, --disable disable single-stepping by default; use\n"
|
||||||
|
" OutputDebugString (\"ssp on\") to enable stepping\n"
|
||||||
|
" -e, --enable enable single-stepping by default; use\n"
|
||||||
|
" OutputDebugString (\"ssp off\") to disable stepping\n"
|
||||||
|
" -h, --help output usage information and exit\n"
|
||||||
|
" -l, --dll enable dll profiling. A chart of relative DLL usage\n"
|
||||||
|
" is produced after the run.\n"
|
||||||
|
" -s, --sub-threads trace sub-threads too. Dangerous if you have\n"
|
||||||
|
" race conditions.\n"
|
||||||
|
" -t, --trace-eip trace every EIP value to a file TRACE.SSP. This\n"
|
||||||
|
" gets big *fast*.\n"
|
||||||
|
" -v, --verbose output verbose messages about debug events.\n"
|
||||||
|
" -V, --version output version information and exit\n"
|
||||||
|
"\n"
|
||||||
|
"Example: %s 0x401000 0x403000 hello.exe\n"
|
||||||
|
"\n"
|
||||||
|
"", prog_name, prog_name);
|
||||||
|
if (stream == stdout)
|
||||||
|
fprintf (stream , ""
|
||||||
|
"SSP - The Single Step Profiler\n"
|
||||||
|
"\n"
|
||||||
|
"Original Author: DJ Delorie <dj@redhat.com>\n"
|
||||||
|
"\n"
|
||||||
|
"The SSP is a program that uses the Win32 debug API to run a program\n"
|
||||||
|
"one ASM instruction at a time. It records the location of each\n"
|
||||||
|
"instruction used, how many times that instruction is used, and all\n"
|
||||||
|
"function calls. The results are saved in a format that is usable by\n"
|
||||||
|
"the profiling program \"gprof\", although gprof will claim the values\n"
|
||||||
|
"are seconds, they really are instruction counts. More on that later.\n"
|
||||||
|
"\n"
|
||||||
|
"Because the SSP was originally designed to profile the cygwin DLL, it\n"
|
||||||
|
"does not automatically select a block of code to report statistics on.\n"
|
||||||
|
"You must specify the range of memory addresses to keep track of\n"
|
||||||
|
"manually, but it's not hard to figure out what to specify. Use the\n"
|
||||||
|
"\"objdump\" program to determine the bounds of the target's \".text\"\n"
|
||||||
|
"section. Let's say we're profiling cygwin1.dll. Make sure you've\n"
|
||||||
|
"built it with debug symbols (else gprof won't run) and run objdump\n"
|
||||||
|
"like this:\n"
|
||||||
|
"\n"
|
||||||
|
" objdump -h cygwin1.dll\n"
|
||||||
|
"\n"
|
||||||
|
"It will print a report like this:\n"
|
||||||
|
"\n"
|
||||||
|
"cygwin1.dll: file format pei-i386\n"
|
||||||
|
"\n"
|
||||||
|
"Sections:\n"
|
||||||
|
"Idx Name Size VMA LMA File off Algn\n"
|
||||||
|
" 0 .text 0007ea00 61001000 61001000 00000400 2**2\n"
|
||||||
|
" CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA\n"
|
||||||
|
" 1 .data 00008000 61080000 61080000 0007ee00 2**2\n"
|
||||||
|
" CONTENTS, ALLOC, LOAD, DATA\n"
|
||||||
|
" . . .\n"
|
||||||
|
"\n"
|
||||||
|
"The only information we're concerned with are the VMA of the .text\n"
|
||||||
|
"section and the VMA of the section after it (sections are usually\n"
|
||||||
|
"contiguous; you can also add the Size to the VMA to get the end\n"
|
||||||
|
"address). In this case, the VMA is 0x61001000 and the ending address\n"
|
||||||
|
"is either 0x61080000 (start of .data method) or 0x0x6107fa00 (VMA+Size\n"
|
||||||
|
"method).\n"
|
||||||
|
"\n"
|
||||||
|
"There are two basic ways to use SSP - either profiling a whole\n"
|
||||||
|
"program, or selectively profiling parts of the program.\n"
|
||||||
|
"\n"
|
||||||
|
"To profile a whole program, just run ssp without options. By default,\n"
|
||||||
|
"it will step the whole program. Here's a simple example, using the\n"
|
||||||
|
"numbers above:\n"
|
||||||
|
"\n"
|
||||||
|
" ssp 0x61001000 0x61080000 hello.exe\n"
|
||||||
|
"\n"
|
||||||
|
"This will step the whole program. It will take at least 8 minutes on\n"
|
||||||
|
"a PII/300 (yes, really). When it's done, it will create a file called\n"
|
||||||
|
"\"gmon.out\". You can turn this data file into a readable report with\n"
|
||||||
|
"gprof:\n"
|
||||||
|
"\n"
|
||||||
|
" gprof -b cygwin1.dll\n"
|
||||||
|
"\n"
|
||||||
|
"The \"-b\" means 'skip the help pages'. You can omit this until you're\n"
|
||||||
|
"familiar with the report layout. The gprof documentation explains\n"
|
||||||
|
"a lot about this report, but ssp changes a few things. For example,\n"
|
||||||
|
"the first part of the report reports the amount of time spent in each\n"
|
||||||
|
"function, like this:\n"
|
||||||
|
"\n"
|
||||||
|
"Each sample counts as 0.01 seconds.\n"
|
||||||
|
" %% cumulative self self total\n"
|
||||||
|
" time seconds seconds calls ms/call ms/call name\n"
|
||||||
|
" 10.02 231.22 72.43 46 1574.57 1574.57 strcspn\n"
|
||||||
|
" 7.95 288.70 57.48 130 442.15 442.15 strncasematch\n"
|
||||||
|
"\n"
|
||||||
|
"The \"seconds\" columns are really CPU opcodes, 1/100 second per opcode.\n"
|
||||||
|
"So, \"231.22\" above means 23,122 opcodes. The ms/call values are 10x\n"
|
||||||
|
"too big; 1574.57 means 157.457 opcodes per call. Similar adjustments\n"
|
||||||
|
"need to be made for the \"self\" and \"children\" columns in the second\n"
|
||||||
|
"part of the report.\n"
|
||||||
|
"\n"
|
||||||
|
"OK, so now we've got a huge report that took a long time to generate,\n"
|
||||||
|
"and we've identified a spot we want to work on optimizing. Let's say\n"
|
||||||
|
"it's the time() function. We can use SSP to selectively profile this\n"
|
||||||
|
"function by using OutputDebugString() to control SSP from within the\n"
|
||||||
|
"program. Here's a sample program:\n"
|
||||||
|
"\n"
|
||||||
|
" #include <windows.h>\n"
|
||||||
|
" main()\n"
|
||||||
|
" {\n"
|
||||||
|
" time_t t;\n"
|
||||||
|
" OutputDebugString(\"ssp on\");\n"
|
||||||
|
" time(&t);\n"
|
||||||
|
" OutputDebugString(\"ssp off\");\n"
|
||||||
|
" }\n"
|
||||||
|
"\n"
|
||||||
|
"Then, add the \"-d\" option to ssp to default to *disabling* profiling.\n"
|
||||||
|
"The program will run at full speed until the first OutputDebugString,\n"
|
||||||
|
"then step until the second.\n"
|
||||||
|
"\n"
|
||||||
|
" ssp -d 0x61001000 0x61080000 hello.exe\n"
|
||||||
|
"\n"
|
||||||
|
"You can then use gprof (as usual) to see the performance profile for\n"
|
||||||
|
"just that portion of the program's execution.\n"
|
||||||
|
"\n"
|
||||||
|
"There are many options to ssp. Since step-profiling makes your\n"
|
||||||
|
"program run about 1,000 times slower than normal, it's best to\n"
|
||||||
|
"understand all the options so that you can narrow down the parts\n"
|
||||||
|
"of your program you need to single-step.\n"
|
||||||
|
"\n"
|
||||||
|
"\"-v\" - verbose. This prints messages about threads starting and\n"
|
||||||
|
"stopping, OutputDebugString calls, DLLs loading, etc.\n"
|
||||||
|
"\n"
|
||||||
|
"\"-t\" and \"-c\" - tracing. With -t, *every* step's address is written\n"
|
||||||
|
"to the file \"trace.ssp\". This can be used to help debug functions,\n"
|
||||||
|
"since it can trace multiple threads. Clever use of scripts can match\n"
|
||||||
|
"addresses with disassembled opcodes if needed. Warning: creates\n"
|
||||||
|
"*huge* files, very quickly. \"-c\" prints each address to the console,\n"
|
||||||
|
"useful for debugging key chunks of assembler.\n"
|
||||||
|
"Use \"addr2line -C -f -s -e foo.exe < trace.ssp > lines.ssp\" and then\n"
|
||||||
|
"\"perl cvttrace\" to convert to symbolic traces.\n"
|
||||||
|
"\n"
|
||||||
|
"\"-s\" - subthreads. Usually, you only need to trace the main thread,\n"
|
||||||
|
"but sometimes you need to trace all threads, so this enables that.\n"
|
||||||
|
"It's also needed when you want to profile a function that only a\n"
|
||||||
|
"subthread calls. However, using OutputDebugString automatically\n"
|
||||||
|
"enables profiling on the thread that called it, not the main thread.\n"
|
||||||
|
"\n"
|
||||||
|
"\"-l\" - dll profiling. Generates a pretty table of how much time was\n"
|
||||||
|
"spent in each dll the program used. No sense optimizing a function in\n"
|
||||||
|
"your program if most of the time is spent in the DLL.\n"
|
||||||
|
"\n"
|
||||||
|
"I usually use the -v, -s, and -l options:\n"
|
||||||
|
"\n"
|
||||||
|
" ssp -v -s -l -d 0x61001000 0x61080000 hello.exe\n"
|
||||||
|
"\n");
|
||||||
|
if (stream == stderr)
|
||||||
|
fprintf (stream, "Try '%s --help' for more information.", prog_name);
|
||||||
|
exit (stream == stderr ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_version ()
|
||||||
|
{
|
||||||
|
const char *v = strchr (version, ':');
|
||||||
|
int len;
|
||||||
|
if (!v)
|
||||||
|
{
|
||||||
|
v = "?";
|
||||||
|
len = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v += 2;
|
||||||
|
len = strchr (v, ' ') - v;
|
||||||
|
}
|
||||||
|
printf ("\
|
||||||
|
%s (cygwin) %.*s\n\
|
||||||
|
Single-Step Profiler\n\
|
||||||
|
Copyright 2000, 2001, 2002 Red Hat, Inc.\n\
|
||||||
|
Compiled on %s", prog_name, len, v, __DATE__);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i, n;
|
int c, i;
|
||||||
int total_pcount, total_scount;
|
int total_pcount, total_scount;
|
||||||
FILE *gmon;
|
FILE *gmon;
|
||||||
|
|
||||||
setbuf (stdout, 0);
|
setbuf (stdout, 0);
|
||||||
|
|
||||||
cmd_line = GetCommandLine ();
|
prog_name = strrchr (argv[0], '/');
|
||||||
|
if (prog_name == NULL)
|
||||||
|
prog_name = strrchr (argv[0], '\\');
|
||||||
|
if (prog_name == NULL)
|
||||||
|
prog_name = argv[0];
|
||||||
|
else
|
||||||
|
prog_name++;
|
||||||
|
|
||||||
/* strip off the program part */
|
while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
|
||||||
n = sscanf (cmd_line, "%*s%n", &i);
|
switch (c)
|
||||||
cmd_line += i;
|
|
||||||
WHITE (cmd_line);
|
|
||||||
|
|
||||||
/* parse arguments. Ugly, but we need to preserve the tail of the line */
|
|
||||||
while (*cmd_line == '-')
|
|
||||||
{
|
{
|
||||||
char opt[100];
|
case 'c':
|
||||||
n = sscanf (cmd_line, " %s%n", opt, &i);
|
printf ("tracing *all* $eip to the console\n");
|
||||||
cmd_line += i;
|
trace_console = 1;
|
||||||
WHITE (cmd_line);
|
break;
|
||||||
|
case 'd':
|
||||||
if (strcmp (opt, "-d") == 0)
|
printf ("stepping disabled; enable via OutputDebugString (\"ssp on\")\n");
|
||||||
{
|
stepping_enabled = 0;
|
||||||
printf ("stepping disabled; enable via OutputDebugString (\"ssp on\")\n");
|
break;
|
||||||
stepping_enabled = 0;
|
case 'e':
|
||||||
}
|
printf ("stepping enabled; disable via OutputDebugString (\"ssp off\")\n");
|
||||||
if (strcmp (opt, "-e") == 0)
|
stepping_enabled = 1;
|
||||||
{
|
break;
|
||||||
printf ("stepping enabled; disable via OutputDebugString (\"ssp off\")\n");
|
case 'h':
|
||||||
stepping_enabled = 1;
|
usage (stdout);
|
||||||
}
|
break;
|
||||||
if (strcmp (opt, "-t") == 0)
|
case 'l':
|
||||||
{
|
printf ("profiling dll usage\n");
|
||||||
printf ("tracing all $eip to trace.ssp\n");
|
dll_counts = 1;
|
||||||
tracing_enabled = 1;
|
break;
|
||||||
}
|
case 's':
|
||||||
if (strcmp (opt, "-tc") == 0)
|
printf ("tracing all sub-threads too, not just the main one\n");
|
||||||
{
|
trace_all_threads = 1;
|
||||||
printf ("tracing *all* $eip to the console\n");
|
break;
|
||||||
trace_console = 1;
|
case 't':
|
||||||
}
|
printf ("tracing all $eip to trace.ssp\n");
|
||||||
if (strcmp (opt, "-s") == 0)
|
tracing_enabled = 1;
|
||||||
{
|
break;
|
||||||
printf ("tracing all sub-threads too, not just the main one\n");
|
case 'v':
|
||||||
trace_all_threads = 1;
|
printf ("verbose messages enabled\n");
|
||||||
}
|
verbose = 1;
|
||||||
if (strcmp (opt, "-dll") == 0)
|
break;
|
||||||
{
|
case 'V':
|
||||||
printf ("profiling dll usage\n");
|
print_version ();
|
||||||
dll_counts = 1;
|
exit (0);
|
||||||
}
|
default:
|
||||||
if (strcmp (opt, "-v") == 0)
|
usage (stderr);
|
||||||
{
|
|
||||||
printf ("verbose messages enabled\n");
|
|
||||||
verbose = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
n = sscanf (cmd_line, " %i %i %n", &low_pc, &high_pc, &i);
|
if ( (argc - optind) < 3 )
|
||||||
if (n < 2)
|
usage (stderr);
|
||||||
{
|
sscanf (argv[optind++], "%i", &low_pc);
|
||||||
fputs (help_text, stderr);
|
sscanf (argv[optind++], "%i", &high_pc);
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
cmd_line += i;
|
|
||||||
|
|
||||||
if (low_pc > high_pc-8)
|
if (low_pc > high_pc-8)
|
||||||
{
|
{
|
||||||
@ -752,9 +898,9 @@ main (int argc, char **argv)
|
|||||||
memset (hits, 0, high_pc-low_pc+4);
|
memset (hits, 0, high_pc-low_pc+4);
|
||||||
|
|
||||||
fprintf (stderr, "prun: [%08x,%08x] Running `%s'\n",
|
fprintf (stderr, "prun: [%08x,%08x] Running `%s'\n",
|
||||||
low_pc, high_pc, cmd_line);
|
low_pc, high_pc, argv[optind]);
|
||||||
|
|
||||||
run_program (cmd_line);
|
run_program (argv[optind]);
|
||||||
|
|
||||||
hdr.lpc = low_pc;
|
hdr.lpc = low_pc;
|
||||||
hdr.hpc = high_pc;
|
hdr.hpc = high_pc;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user