OSDN Git Service

* utils.sgml (kill): Add SIGIO, SIGCLD, and SIGPWR.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / ssp.c
1 /*
2  * Copyright (c) 2000, 2001, 2002, 2009 Red Hat, Inc.
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     A copy of the GNU General Public License can be found at
10  *     http://www.gnu.org/
11  *
12  * Written by DJ Delorie <dj@redhat.com>
13  *
14  */
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <time.h>
21 #include <ctype.h>
22 #include <windows.h>
23 #include <getopt.h>
24
25 static const char version[] = "$Revision$";
26 static char *prog_name;
27
28 static struct option longopts[] =
29 {
30   {"console-trace", no_argument, NULL, 'c' },
31   {"disable", no_argument, NULL, 'd' },
32   {"enable", no_argument, NULL, 'e' },
33   {"help", no_argument, NULL, 'h' },
34   {"dll", no_argument, NULL, 'l' },
35   {"sub-threads", no_argument, NULL, 's' },
36   {"trace-eip", no_argument, NULL, 't' },
37   {"verbose", no_argument, NULL, 'v' },
38   {"version", no_argument, NULL, 'V' },
39   {NULL, 0, NULL, 0}
40 };
41
42 static char opts[] = "+cdehlstvV";
43
44 #define KERNEL_ADDR 0x77000000
45
46 #define TRACE_SSP 0
47
48 #define VERBOSE 1
49 #define TIMES   1000
50
51 /* from winsup/gmon.h */
52 struct gmonhdr {
53         unsigned long   lpc;    /* base pc address of sample buffer */
54         unsigned long   hpc;    /* max pc address of sampled buffer */
55         int     ncnt;           /* size of sample buffer (plus this header) */
56         int     version;        /* version number */
57         int     profrate;       /* profiling clock rate */
58         int     spare[3];       /* reserved */
59 };
60 #define GMONVERSION     0x00051879
61 #define HISTCOUNTER unsigned short
62
63 typedef struct {
64   unsigned int base_address;
65   int pcount;
66   int scount;
67   char *name;
68 } DllInfo;
69
70 typedef struct {
71   unsigned int address;
72   unsigned char real_byte;
73 } PendingBreakpoints;
74
75 unsigned low_pc=0, high_pc=0;
76 unsigned last_pc=0, pc, last_sp=0, sp;
77 int total_cycles, count;
78 HANDLE hProcess;
79 PROCESS_INFORMATION procinfo;
80 STARTUPINFO startup;
81 CONTEXT context;
82 HISTCOUNTER *hits=0;
83 struct gmonhdr hdr;
84 int running = 1, profiling = 1;
85 char dll_name[1024], *dll_ptr, *cp;
86 int eip;
87 unsigned opcode_count = 0;
88
89 int stepping_enabled = 1;
90 int tracing_enabled = 0;
91 int trace_console = 0;
92 int trace_all_threads = 0;
93 int dll_counts = 0;
94 int verbose = 0;
95
96 #define MAXTHREADS 100
97 DWORD active_thread_ids[MAXTHREADS];
98 HANDLE active_threads[MAXTHREADS];
99 DWORD thread_step_flags[MAXTHREADS];
100 DWORD thread_return_address[MAXTHREADS];
101 int num_active_threads = 0;
102 int suspended_count=0;
103
104 #define MAXDLLS 100
105 DllInfo dll_info[MAXDLLS];
106 int num_dlls=0;
107
108 #define MAXPENDS 100
109 PendingBreakpoints pending_breakpoints[MAXPENDS];
110 int num_breakpoints=0;
111
112 static void
113 add_breakpoint (unsigned int address)
114 {
115   int i;
116   DWORD rv;
117   static char int3[] = { 0xcc };
118   for (i=0; i<num_breakpoints; i++)
119     {
120       if (pending_breakpoints[i].address == address)
121         return;
122       if (pending_breakpoints[i].address == 0)
123         break;
124     }
125   if (i == MAXPENDS)
126     return;
127   pending_breakpoints[i].address = address;
128   ReadProcessMemory (hProcess,
129                     (void *)address,
130                     &(pending_breakpoints[i].real_byte),
131                     1, &rv);
132
133   WriteProcessMemory (hProcess,
134                      (void *)address,
135                      (LPVOID)int3, 1, &rv);
136   if (i >= num_breakpoints)
137     num_breakpoints = i+1;
138 }
139
140 static int
141 remove_breakpoint (unsigned int address)
142 {
143   int i;
144   DWORD rv;
145   for (i=0; i<num_breakpoints; i++)
146     {
147       if (pending_breakpoints[i].address == address)
148         {
149           pending_breakpoints[i].address = 0;
150           WriteProcessMemory (hProcess,
151                              (void *)address,
152                              &(pending_breakpoints[i].real_byte),
153                              1, &rv);
154           return 1;
155         }
156     }
157   return 0;
158 }
159
160 static HANDLE
161 lookup_thread_id (DWORD threadId, int *tix)
162 {
163   int i;
164   *tix = 0;
165   for (i=0; i<num_active_threads; i++)
166     if (active_thread_ids[i] == threadId)
167       {
168         if (tix)
169           *tix = i;
170         return active_threads[i];
171       }
172   return 0;
173 }
174
175 static void
176 set_step_threads (int threadId, int trace)
177 {
178   int rv, tix;
179   HANDLE thread = lookup_thread_id (threadId, &tix);
180
181   rv = GetThreadContext (thread, &context);
182   if (rv != -1)
183     {
184       thread_step_flags[tix] = trace;
185       if (trace)
186         context.EFlags |= 0x100; /* TRAP (single step) flag */
187       else
188         context.EFlags &= ~0x100; /* TRAP (single step) flag */
189       SetThreadContext (thread, &context);
190     }
191 }
192
193 static void
194 set_steps ()
195 {
196   int i, s;
197   for (i=0; i<num_active_threads; i++)
198     {
199       GetThreadContext (active_threads[i], &context);
200       s = context.EFlags & 0x0100;
201       if (!s && thread_step_flags[i])
202         {
203           set_step_threads (active_thread_ids[i], 1);
204         }
205     }
206 }
207
208 static int
209 dll_sort (const void *va, const void *vb)
210 {
211   DllInfo *a = (DllInfo *)va;
212   DllInfo *b = (DllInfo *)vb;
213   if (a->base_address < b->base_address)
214     return -1;
215   return 1;
216 }
217
218 static char *
219 addr2dllname (unsigned int addr)
220 {
221   int i;
222   for (i=num_dlls-1; i>=0; i--)
223     {
224       if (dll_info[i].base_address < addr)
225         {
226           return dll_info[i].name;
227         }
228     }
229   return (char *)"";
230 }
231
232 static void
233 dump_registers (HANDLE thread)
234 {
235   context.ContextFlags = CONTEXT_FULL;
236   GetThreadContext (thread, &context);
237   printf ("eax %08lx ebx %08lx ecx %08lx edx %08lx eip\n",
238           context.Eax, context.Ebx, context.Ecx, context.Edx);
239   printf ("esi %08lx edi %08lx ebp %08lx esp %08lx %08lx\n",
240           context.Esi, context.Esi, context.Ebp, context.Esp, context.Eip);
241 }
242
243 typedef struct Edge {
244   struct Edge *next;
245   unsigned int from_pc;
246   unsigned int to_pc;
247   unsigned int count;
248 } Edge;
249
250 Edge *edges[4096];
251
252 void
253 store_call_edge (unsigned int from_pc, unsigned int to_pc)
254 {
255   Edge *e;
256   unsigned int h = ((from_pc + to_pc)>>4) & 4095;
257   for (e=edges[h]; e; e=e->next)
258     if (e->from_pc == from_pc && e->to_pc == to_pc)
259       break;
260   if (!e)
261     {
262       e = (Edge *)malloc (sizeof (Edge));
263       e->next = edges[h];
264       edges[h] = e;
265       e->from_pc = from_pc;
266       e->to_pc = to_pc;
267       e->count = 0;
268     }
269   e->count++;
270 }
271
272 void
273 write_call_edges (FILE *f)
274 {
275   int h;
276   Edge *e;
277   for (h=0; h<4096; h++)
278     for (e=edges[h]; e; e=e->next)
279       fwrite (&(e->from_pc), 1, 3*sizeof (unsigned int), f);
280 }
281
282 char *
283 wide_strdup (char *cp)
284 {
285   unsigned short *s = (unsigned short *)cp;
286   int len;
287   char *rv;
288   for (len=0; s[len]; len++);
289   rv = (char *)malloc (len+1);
290   for (len=0; s[len]; len++)
291     rv[len] = s[len];
292   rv[len] = 0;
293   return rv;
294 }
295
296 void
297 run_program (char *cmdline)
298 {
299   FILE *tracefile = 0;
300   int tix, i;
301   HANDLE hThread;
302   char *string;
303
304   memset (&startup, 0, sizeof (startup));
305   startup.cb = sizeof (startup);
306
307   if (!CreateProcess (0, cmdline, 0, 0, 0,
308                      CREATE_NEW_PROCESS_GROUP
309                      | CREATE_SUSPENDED
310                      | DEBUG_PROCESS
311                      | DEBUG_ONLY_THIS_PROCESS,
312                      0, 0, &startup, &procinfo))
313     {
314       fprintf (stderr, "Can't create process: error %ld\n", GetLastError ());
315       exit (1);
316     }
317
318   hProcess = procinfo.hProcess;
319 #if 0
320   printf ("procinfo: %08x %08x %08x %08x\n",
321          hProcess, procinfo.hThread, procinfo.dwProcessId, procinfo.dwThreadId);
322 #endif
323
324   active_threads[0] = procinfo.hThread;
325   active_thread_ids[0] = procinfo.dwThreadId;
326   thread_step_flags[0] = stepping_enabled;
327   num_active_threads = 1;
328
329   dll_info[0].base_address = 0;
330   dll_info[0].pcount = 0;
331   dll_info[0].scount = 0;
332   dll_info[0].name = cmdline;
333   num_dlls = 1;
334
335   SetThreadPriority (procinfo.hThread, THREAD_PRIORITY_IDLE);
336
337   context.ContextFlags = CONTEXT_FULL;
338
339   ResumeThread (procinfo.hThread);
340
341   total_cycles = 0;
342
343   if (tracing_enabled)
344     {
345       tracefile = fopen ("trace.ssp", "w");
346       if (!tracefile)
347         {
348           tracing_enabled = 0;
349           perror ("trace.ssp");
350         }
351     }
352
353   running = 1;
354   while (running)
355     {
356       int src, dest;
357       DWORD rv;
358       DEBUG_EVENT event;
359       int contv = DBG_CONTINUE;
360
361       event.dwDebugEventCode = -1;
362       if (!WaitForDebugEvent (&event, INFINITE))
363         {
364           printf ("idle...\n");
365         }
366
367       hThread = lookup_thread_id (event.dwThreadId, &tix);
368
369 #if 0
370       printf ("DE: %x/%d %d %d ",
371              hThread, tix,
372              event.dwDebugEventCode, num_active_threads);
373       for (src=0; src<num_active_threads; src++)
374         {
375           int sc = SuspendThread (active_threads[src]);
376           int rv = GetThreadContext (active_threads[src], &context);
377           ResumeThread (active_threads[src]);
378           printf (" [%x,%x,%x]",
379                  active_threads[src], context.Eip, active_thread_ids[src]);
380         }
381       printf ("\n");
382 #endif
383
384       switch (event.dwDebugEventCode)
385         {
386
387         case CREATE_PROCESS_DEBUG_EVENT:
388           break;
389
390         case CREATE_THREAD_DEBUG_EVENT:
391           if (verbose)
392             printf ("create thread %08lx at %08x %s\n",
393                    event.dwThreadId,
394                    (int)event.u.CreateThread.lpStartAddress,
395                    addr2dllname ((unsigned int)event.u.CreateThread.lpStartAddress));
396
397           active_thread_ids[num_active_threads] = event.dwThreadId;
398           active_threads[num_active_threads] = event.u.CreateThread.hThread;
399           thread_return_address[num_active_threads] = 0;
400           num_active_threads++;
401
402           if (trace_all_threads && stepping_enabled)
403             {
404               thread_step_flags[num_active_threads-1] = stepping_enabled;
405               add_breakpoint ((int)event.u.CreateThread.lpStartAddress);
406             }
407
408           break;
409
410         case EXIT_THREAD_DEBUG_EVENT:
411           if (verbose)
412             printf ("exit thread %08lx, code=%ld\n",
413                    event.dwThreadId,
414                    event.u.ExitThread.dwExitCode);
415
416           for (src=0, dest=0; src<num_active_threads; src++)
417             if (active_thread_ids[src] != event.dwThreadId)
418               {
419                 active_thread_ids[dest] = active_thread_ids[src];
420                 active_threads[dest] = active_threads[src];
421                 dest++;
422               }
423           num_active_threads = dest;
424           break;
425
426         case EXCEPTION_DEBUG_EVENT:
427           rv = GetThreadContext (hThread, &context);
428           switch (event.u.Exception.ExceptionRecord.ExceptionCode)
429             {
430             case STATUS_BREAKPOINT:
431               if (remove_breakpoint ((int)event.u.Exception.ExceptionRecord.ExceptionAddress))
432                 {
433                   context.Eip --;
434                   if (!rv)
435                     SetThreadContext (hThread, &context);
436                   if (ReadProcessMemory (hProcess, (void *)context.Esp, &rv, 4, &rv))
437                       thread_return_address[tix] = rv;
438                 }
439               set_step_threads (event.dwThreadId, stepping_enabled);
440             case STATUS_SINGLE_STEP:
441               opcode_count++;
442               pc = (unsigned int)event.u.Exception.ExceptionRecord.ExceptionAddress;
443               sp = (unsigned int)context.Esp;
444               if (tracing_enabled)
445                 fprintf (tracefile, "%08x %08lx\n", pc, event.dwThreadId);
446               if (trace_console)
447                 {
448                   printf ("%d %08x\n", tix, pc);
449                   fflush (stdout);
450                 }
451
452               if (dll_counts)
453                 {
454                   int i;
455                   for (i=num_dlls-1; i>=0; i--)
456                     {
457                       if (dll_info[i].base_address < context.Eip)
458                         {
459                           if (hThread == procinfo.hThread)
460                             dll_info[i].pcount++;
461                           else
462                             dll_info[i].scount++;
463                           break;
464                         }
465                     }
466                 }
467
468               if (pc < last_pc || pc > last_pc+10)
469                 {
470                   static int ncalls=0;
471                   static int qq=0;
472                   if (++qq % 100 == 0)
473                     fprintf (stderr, " %08x %d %d \r",
474                             pc, ncalls, opcode_count);
475
476                   if (sp == last_sp-4)
477                     {
478                       ncalls++;
479                       store_call_edge (last_pc, pc);
480                       if (last_pc < KERNEL_ADDR && pc > KERNEL_ADDR)
481                         {
482                           int retaddr;
483                           DWORD rv;
484                           ReadProcessMemory (hProcess,
485                                             (void *)sp,
486                                             (LPVOID)&(retaddr),
487                                             4, &rv);
488 #if 0
489                           printf ("call last_pc = %08x pc = %08x rv = %08x\n",
490                                  last_pc, pc, retaddr);
491                           /* experimental - try to skip kernel calls for speed */
492                           add_breakpoint (retaddr);
493                           set_step_threads (event.dwThreadId, 0);
494 #endif
495                         }
496                     }
497                 }
498
499               total_cycles++;
500               last_sp = sp;
501               last_pc = pc;
502               if (pc >= low_pc && pc < high_pc)
503                 hits[(pc - low_pc)/2] ++;
504               break;
505             default:
506               if (verbose)
507                 {
508                   printf ("exception %ld, ", event.u.Exception.dwFirstChance);
509                   printf ("code: %lx flags: %lx\n",
510                          event.u.Exception.ExceptionRecord.ExceptionCode,
511                          event.u.Exception.ExceptionRecord.ExceptionFlags);
512                   if (event.u.Exception.dwFirstChance == 1)
513                     dump_registers (hThread);
514                 }
515               contv = DBG_EXCEPTION_NOT_HANDLED;
516               running = 0;
517               break;
518             }
519
520           if (!rv)
521             {
522               if (pc == thread_return_address[tix])
523                 {
524                   if (context.EFlags & 0x100)
525                     {
526                       context.EFlags &= ~0x100; /* TRAP (single step) flag */
527                       SetThreadContext (hThread, &context);
528                     }
529                 }
530               else if (stepping_enabled)
531                 {
532                   if (!(context.EFlags & 0x100))
533                     {
534                       context.EFlags |= 0x100; /* TRAP (single step) flag */
535                       SetThreadContext (hThread, &context);
536                     }
537                 }
538             }
539           break;
540
541         case OUTPUT_DEBUG_STRING_EVENT:
542           string = (char *)malloc (event.u.DebugString.nDebugStringLength+1);
543           i = ReadProcessMemory (hProcess,
544                             event.u.DebugString.lpDebugStringData,
545                             (LPVOID)string,
546                             event.u.DebugString.nDebugStringLength,
547                             &rv);
548           if (!i)
549             {
550               printf ("error reading memory: %ld %ld\n", rv, GetLastError ());
551             }
552           if (verbose)
553             printf ("ODS: %x/%d \"%s\"\n",
554                    (int)hThread, tix, string);
555
556           if (strcmp (string, "ssp on") == 0)
557             {
558               stepping_enabled = 1;
559               set_step_threads (event.dwThreadId, 1);
560             }
561
562           if (strcmp (string, "ssp off") == 0)
563             {
564               stepping_enabled = 0;
565               set_step_threads (event.dwThreadId, 0);
566             }
567
568           break;
569
570
571         case LOAD_DLL_DEBUG_EVENT:
572           if (verbose)
573             printf ("load dll %08x:",
574                    (int)event.u.LoadDll.lpBaseOfDll);
575
576           dll_ptr = (char *)"( u n k n o w n ) \0\0";
577           if (event.u.LoadDll.lpImageName)
578             {
579               ReadProcessMemory (hProcess,
580                                 event.u.LoadDll.lpImageName,
581                                 (LPVOID)&src,
582                                 sizeof (src),
583                                 &rv);
584               if (src)
585                 {
586                   ReadProcessMemory (hProcess,
587                                     (void *)src,
588                                     (LPVOID)dll_name,
589                                     sizeof (dll_name),
590                                     &rv);
591                   dll_name[rv] = 0;
592                   dll_ptr = dll_name;
593                   for (cp=dll_name; *cp; cp++)
594                     {
595                       if (*cp == '\\' || *cp == '/')
596                         {
597                           dll_ptr = cp+1;
598                         }
599                       *cp = tolower ((unsigned char) *cp);
600                     }
601                 }
602             }
603
604
605           dll_info[num_dlls].base_address
606             = (unsigned int)event.u.LoadDll.lpBaseOfDll;
607           dll_info[num_dlls].pcount = 0;
608           dll_info[num_dlls].scount = 0;
609           dll_info[num_dlls].name = wide_strdup (dll_ptr);
610           if (verbose)
611             printf (" %s\n", dll_info[num_dlls].name);
612           num_dlls++;
613           qsort (dll_info, num_dlls, sizeof (DllInfo), dll_sort);
614
615           break;
616
617         case UNLOAD_DLL_DEBUG_EVENT:
618           if (verbose)
619             printf ("unload dll\n");
620           break;
621
622         case EXIT_PROCESS_DEBUG_EVENT:
623           if (verbose)
624             printf ("process %08lx %08lx exit %ld\n",
625                    event.dwProcessId, event.dwThreadId,
626                    event.u.ExitProcess.dwExitCode);
627
628           running = 0;
629           break;
630         }
631
632       set_steps ();
633       ContinueDebugEvent (event.dwProcessId, event.dwThreadId, contv);
634     }
635
636   count = 0;
637   for (pc=low_pc; pc<high_pc; pc+=2)
638     {
639       count += hits[(pc - low_pc)/2];
640     }
641   printf ("total cycles: %d, counted cycles: %d\n", total_cycles, count);
642
643   if (tracing_enabled)
644     fclose (tracefile);
645
646 }
647
648 static void
649 usage (FILE * stream)
650 {
651   fprintf (stream , ""
652   "Usage: %s [options] low_pc high_pc command...\n"
653   "Single-step profile COMMAND\n"
654   "\n"
655   " -c, --console-trace  trace every EIP value to the console. *Lots* slower.\n"
656   " -d, --disable        disable single-stepping by default; use\n"
657   "                      OutputDebugString (\"ssp on\") to enable stepping\n"
658   " -e, --enable         enable single-stepping by default; use\n"
659   "                      OutputDebugString (\"ssp off\") to disable stepping\n"
660   " -h, --help           output usage information and exit\n"
661   " -l, --dll            enable dll profiling.  A chart of relative DLL usage\n"
662   "                      is produced after the run.\n"
663   " -s, --sub-threads    trace sub-threads too.  Dangerous if you have\n" 
664   "                      race conditions.\n"
665   " -t, --trace-eip      trace every EIP value to a file TRACE.SSP.  This\n"
666   "                      gets big *fast*.\n"
667   " -v, --verbose        output verbose messages about debug events.\n"
668   " -V, --version        output version information and exit\n"
669   "\n"
670   "Example: %s 0x401000 0x403000 hello.exe\n"
671   "\n"
672   "", prog_name, prog_name);
673   if (stream == stdout)
674     fprintf (stream , ""
675     "SSP - The Single Step Profiler\n"
676     "\n"
677     "Original Author:  DJ Delorie <dj@redhat.com>\n"
678     "\n"
679     "The SSP is a program that uses the Win32 debug API to run a program\n"
680     "one ASM instruction at a time.  It records the location of each\n"
681     "instruction used, how many times that instruction is used, and all\n"
682     "function calls.  The results are saved in a format that is usable by\n"
683     "the profiling program \"gprof\", although gprof will claim the values\n"
684     "are seconds, they really are instruction counts.  More on that later.\n"
685     "\n"
686     "Because the SSP was originally designed to profile the cygwin DLL, it\n"
687     "does not automatically select a block of code to report statistics on.\n"
688     "You must specify the range of memory addresses to keep track of\n"
689     "manually, but it's not hard to figure out what to specify.  Use the\n"
690     "\"objdump\" program to determine the bounds of the target's \".text\"\n"
691     "section.  Let's say we're profiling cygwin1.dll.  Make sure you've\n"
692     "built it with debug symbols (else gprof won't run) and run objdump\n"
693     "like this:\n"
694     "\n"
695     "   objdump -h cygwin1.dll\n"
696     "\n"
697     "It will print a report like this:\n"
698     "\n"
699     "cygwin1.dll:     file format pei-i386\n"
700     "\n"
701     "Sections:\n"
702     "Idx Name          Size      VMA       LMA       File off  Algn\n"
703     "  0 .text         0007ea00  61001000  61001000  00000400  2**2\n"
704     "                  CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA\n"
705     "  1 .data         00008000  61080000  61080000  0007ee00  2**2\n"
706     "                  CONTENTS, ALLOC, LOAD, DATA\n"
707     "  . . .\n"
708     "\n"
709     "The only information we're concerned with are the VMA of the .text\n"
710     "section and the VMA of the section after it (sections are usually\n"
711     "contiguous; you can also add the Size to the VMA to get the end\n"
712     "address).  In this case, the VMA is 0x61001000 and the ending address\n"
713     "is either 0x61080000 (start of .data method) or 0x0x6107fa00 (VMA+Size\n"
714     "method).\n"
715     "\n"
716     "There are two basic ways to use SSP - either profiling a whole\n"
717     "program, or selectively profiling parts of the program.\n"
718     "\n"
719     "To profile a whole program, just run ssp without options.  By default,\n"
720     "it will step the whole program.  Here's a simple example, using the\n"
721     "numbers above:\n"
722     "\n"
723     "   ssp 0x61001000 0x61080000 hello.exe\n"
724     "\n"
725     "This will step the whole program.  It will take at least 8 minutes on\n"
726     "a PII/300 (yes, really).  When it's done, it will create a file called\n"
727     "\"gmon.out\".  You can turn this data file into a readable report with\n"
728     "gprof:\n"
729     "\n"
730     "   gprof -b cygwin1.dll\n"
731     "\n"
732     "The \"-b\" means 'skip the help pages'.  You can omit this until you're\n"
733     "familiar with the report layout.  The gprof documentation explains\n"
734     "a lot about this report, but ssp changes a few things.  For example,\n"
735     "the first part of the report reports the amount of time spent in each\n"
736     "function, like this:\n"
737     "\n"
738     "Each sample counts as 0.01 seconds.\n"
739     "  %%   cumulative   self              self     total\n"
740     " time   seconds   seconds    calls  ms/call  ms/call  name\n"
741     " 10.02    231.22    72.43       46  1574.57  1574.57  strcspn\n"
742     "  7.95    288.70    57.48      130   442.15   442.15  strncasematch\n"
743     "\n"
744     "The \"seconds\" columns are really CPU opcodes, 1/100 second per opcode.\n"
745     "So, \"231.22\" above means 23,122 opcodes.  The ms/call values are 10x\n"
746     "too big; 1574.57 means 157.457 opcodes per call.  Similar adjustments\n"
747     "need to be made for the \"self\" and \"children\" columns in the second\n"
748     "part of the report.\n"
749     "\n"
750     "OK, so now we've got a huge report that took a long time to generate,\n"
751     "and we've identified a spot we want to work on optimizing.  Let's say\n"
752     "it's the time() function.  We can use SSP to selectively profile this\n"
753     "function by using OutputDebugString() to control SSP from within the\n"
754     "program.  Here's a sample program:\n"
755     "\n"
756     "   #include <windows.h>\n"
757     "   main()\n"
758     "   {\n"
759     "     time_t t;\n"
760     "     OutputDebugString(\"ssp on\");\n"
761     "     time(&t);\n"
762     "     OutputDebugString(\"ssp off\");\n"
763     "   }\n"
764     "\n"
765     "Then, add the \"-d\" option to ssp to default to *disabling* profiling.\n"
766     "The program will run at full speed until the first OutputDebugString,\n"
767     "then step until the second.\n"
768     "\n"
769     "   ssp -d 0x61001000 0x61080000 hello.exe\n"
770     "\n"
771     "You can then use gprof (as usual) to see the performance profile for\n"
772     "just that portion of the program's execution.\n"
773     "\n"
774     "There are many options to ssp.  Since step-profiling makes your\n"
775     "program run about 1,000 times slower than normal, it's best to\n"
776     "understand all the options so that you can narrow down the parts\n"
777     "of your program you need to single-step.\n"
778     "\n"
779     "\"-v\" - verbose.  This prints messages about threads starting and\n"
780     "stopping, OutputDebugString calls, DLLs loading, etc.\n"
781     "\n"
782     "\"-t\" and \"-c\" - tracing.  With -t, *every* step's address is written\n"
783     "to the file \"trace.ssp\".  This can be used to help debug functions,\n"
784     "since it can trace multiple threads.  Clever use of scripts can match\n"
785     "addresses with disassembled opcodes if needed.  Warning: creates\n"
786     "*huge* files, very quickly.  \"-c\" prints each address to the console,\n"
787     "useful for debugging key chunks of assembler.\n"
788     "Use \"addr2line -C -f -s -e foo.exe < trace.ssp > lines.ssp\" and then\n"
789     "\"perl cvttrace\" to convert to symbolic traces.\n"
790     "\n"
791     "\"-s\" - subthreads.  Usually, you only need to trace the main thread,\n"
792     "but sometimes you need to trace all threads, so this enables that.\n"
793     "It's also needed when you want to profile a function that only a\n"
794     "subthread calls.  However, using OutputDebugString automatically\n"
795     "enables profiling on the thread that called it, not the main thread.\n"
796     "\n"
797     "\"-l\" - dll profiling.  Generates a pretty table of how much time was\n"
798     "spent in each dll the program used.  No sense optimizing a function in\n"
799     "your program if most of the time is spent in the DLL.\n"
800     "\n"
801     "I usually use the -v, -s, and -l options:\n"
802     "\n"
803     "   ssp -v -s -l -d 0x61001000 0x61080000 hello.exe\n"
804     "\n");
805   if (stream == stderr)
806     fprintf (stream, "Try '%s --help' for more information.\n", prog_name);
807   exit (stream == stderr ? 1 : 0);
808 }
809
810 static void
811 print_version ()
812 {
813   const char *v = strchr (version, ':');
814   int len;
815   if (!v)
816     {
817       v = "?";
818       len = 1;
819     }
820   else
821     {
822       v += 2;
823       len = strchr (v, ' ') - v;
824     }
825   printf ("\
826 %s (cygwin) %.*s\n\
827 Single-Step Profiler\n\
828 Copyright 2000, 2001, 2002 Red Hat, Inc.\n\
829 Compiled on %s\n\
830 ", prog_name, len, v, __DATE__);
831 }
832
833 int
834 main (int argc, char **argv)
835 {
836   int c, i;
837   int total_pcount = 0, total_scount = 0;
838   FILE *gmon;
839
840   setbuf (stdout, 0);
841
842   prog_name = strrchr (argv[0], '/');
843   if (prog_name == NULL)
844     prog_name = strrchr (argv[0], '\\');
845   if (prog_name == NULL)
846     prog_name = argv[0];
847   else
848     prog_name++;
849
850   while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
851     switch (c)
852     {
853       case 'c':
854         printf ("tracing *all* $eip to the console\n");
855         trace_console = 1;
856         break;
857       case 'd':
858         printf ("stepping disabled; enable via OutputDebugString (\"ssp on\")\n");
859         stepping_enabled = 0;
860         break;
861       case 'e':
862         printf ("stepping enabled; disable via OutputDebugString (\"ssp off\")\n");
863         stepping_enabled = 1;
864         break;
865       case 'h':
866         usage (stdout);
867         break;
868       case 'l':
869         printf ("profiling dll usage\n");
870         dll_counts = 1;
871         break;
872       case 's':
873         printf ("tracing all sub-threads too, not just the main one\n");
874         trace_all_threads = 1;
875         break;
876       case 't':
877         printf ("tracing all $eip to trace.ssp\n");
878         tracing_enabled = 1;
879         break;
880       case 'v':
881         printf ("verbose messages enabled\n");
882         verbose = 1;
883         break;
884       case 'V':
885         print_version ();
886         exit (0);
887       default:  
888         usage (stderr);
889     }
890
891   if ( (argc - optind) < 3 )
892     usage (stderr);
893   sscanf (argv[optind++], "%i", &low_pc); 
894   sscanf (argv[optind++], "%i", &high_pc); 
895
896   if (low_pc > high_pc-8)
897     {
898       fprintf (stderr, "Hey, low_pc must be lower than high_pc\n");
899       exit (1);
900     }
901
902   hits = (HISTCOUNTER *)malloc (high_pc-low_pc+4);
903   memset (hits, 0, high_pc-low_pc+4);
904
905   fprintf (stderr, "prun: [%08x,%08x] Running '%s'\n",
906           low_pc, high_pc, argv[optind]);
907
908   run_program (argv[optind]);
909
910   hdr.lpc = low_pc;
911   hdr.hpc = high_pc;
912   hdr.ncnt = high_pc-low_pc + sizeof (hdr);
913   hdr.version = GMONVERSION;
914   hdr.profrate = 100;
915
916   gmon = fopen ("gmon.out", "wb");
917   fwrite (&hdr, 1, sizeof (hdr), gmon);
918   fwrite (hits, 1, high_pc-low_pc, gmon);
919   write_call_edges (gmon);
920   fclose (gmon);
921
922   if (dll_counts)
923     {
924       /*      1234567 123% 1234567 123% 12345678 xxxxxxxxxxx */
925       printf (" Main-Thread Other-Thread BaseAddr DLL Name\n");
926
927       total_pcount = 0;
928       total_scount = 0;
929       for (i=0; i<num_dlls; i++)
930         {
931           total_pcount += dll_info[i].pcount;
932           total_scount += dll_info[i].scount;
933         }
934
935       if (total_pcount == 0) total_pcount++;
936       if (total_scount == 0) total_scount++;
937
938       for (i=0; i<num_dlls; i++)
939         if (dll_info[i].pcount || dll_info[i].scount)
940           {
941             printf ("%7d %3d%% %7d %3d%% %08x %s\n",
942                    dll_info[i].pcount,
943                    (dll_info[i].pcount*100)/opcode_count,
944                    dll_info[i].scount,
945                    (dll_info[i].scount*100)/opcode_count,
946                    dll_info[i].base_address,
947                    dll_info[i].name);
948           }
949     }
950
951   exit (0);
952 }
953