home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 39 / IOPROG_39.ISO / SOFT / sdkjava40.exe / data1.cab / fg_Samples / Samples / Profiler / jviewprf / main.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-04  |  49.5 KB  |  1,825 lines

  1. // main.cpp
  2. //
  3. // (C) Copyright 1995 - 1999 Microsoft Corporation.  All rights reserved.
  4. //
  5.  
  6. #include "project.hpp"
  7. #pragma hdrstop
  8.  
  9. #include <jexefmt.h>
  10. #include <stdio.h>
  11.  
  12. #include "jviewprf.hpp"
  13. #include "utils.hpp"
  14. #include "resource.h"
  15.  
  16. extern HINSTANCE g_hInst;
  17.  
  18. // dllmain.cpp
  19. VOID DisplayUsage ();
  20.  
  21.  
  22. //------------------------------------------------------------------------
  23.  
  24.  
  25. IJVIEWProfilerUtils *g_pJVIEW;
  26.  
  27. ULONG g_nMethodsToHook = 0;
  28. PSTR *g_rgpszMethodsToHook = NULL;
  29. ULONG g_nClassesToHook = 0;
  30. PSTR *g_rgpszClassesToHook = NULL;
  31.  
  32. ULONG g_SamplingFrequency = 100;
  33. BOOL g_fSamplingFrequencySpecified = FALSE;
  34.  
  35. // Bitmask of OPT_XXX flags (jviewprf.hpp).
  36. DWORD g_ProfOptions = 0;
  37.  
  38. EventMonitor *g_EventMonitor = NULL;
  39.  
  40. // All profiling output is written here.  This is either stdout or an output
  41. // file.
  42. HANDLE g_hOutput = INVALID_HANDLE_VALUE;
  43.  
  44. // Filename specified via -prof:file=<filename>.
  45. PSTR g_pszOutputFileName = NULL;
  46.  
  47. // Character width of whatever's on the other end of g_hOutput:
  48. // - For files, this is an arbitrarily bigger number, 120.
  49. // - For a console, this is the console's buffer width.
  50. // - For anything else (ex. redirected stdout), this is 80.
  51. // If OPT_TABLE (-prof:table) is set, this is 0, indicating that things should
  52. // not be spaced out.
  53. ULONG g_OutputWidth;
  54.  
  55. // Set to TRUE if g_hOutput is a console.
  56. BOOL g_fOutputIsConsole = FALSE;
  57.  
  58. // Indicates that WriteOutput should convert "\n" to "\r\n".
  59. #define g_fConvertNewlines (!g_fOutputIsConsole)
  60.  
  61. // Set when FatalError is called.  If EventMonitor::Initialize ends with a
  62. // failed HRESULT, and this is not set, FatalError is called with a default
  63. // "couldn't initialize" error message.
  64. BOOL g_fErrorMessageDisplayed = FALSE;
  65.  
  66.  
  67. //------------------------------------------------------------------------
  68.  
  69.  
  70. VOID FatalError (UINT id, ...)
  71. {
  72.     va_list va;
  73.  
  74.     va_start(va, id);
  75.     DisplayMessage(JDC_ERROR, id, va);
  76.     va_end(va);
  77. }
  78.  
  79.  
  80. VOID Warning (UINT id, ...)
  81. {
  82.     va_list va;
  83.  
  84.     va_start(va, id);
  85.     DisplayMessage(JDC_WARNING, id, va);
  86.     va_end(va);
  87. }
  88.  
  89.  
  90. VOID WriteOutput (PCSTR pcsz, int len)
  91. {
  92.     if (!g_fConvertNewlines)
  93.     {
  94.         DWORD wrote;
  95.         WriteFile(g_hOutput, pcsz, len, &wrote, NULL);
  96.         return;
  97.     }
  98.  
  99.     PCSTR stop = pcsz+len;
  100.  
  101.     for (;;)
  102.     {
  103.         int towrite;
  104.     
  105.         PCSTR nl = pcsz;
  106.         while (nl < stop && *nl != '\n')
  107.             nl++;
  108.         if (nl < stop)
  109.             towrite = nl - pcsz;
  110.         else
  111.             towrite = len;
  112.  
  113.         DWORD wrote;
  114.         WriteFile(g_hOutput, pcsz, towrite, &wrote, NULL);
  115.  
  116.         pcsz += towrite;
  117.         len -= towrite;
  118.  
  119.         if (nl < stop)
  120.         {
  121.             WriteFile(g_hOutput, "\r\n", 2, &wrote, NULL);
  122.             pcsz++;
  123.             len--;
  124.         }
  125.  
  126.         if (!len)
  127.             break;
  128.     }
  129. }
  130.  
  131.  
  132. VOID WriteOutput (PCSTR pcsz)
  133. {
  134.     int len = strlen(pcsz);
  135.     WriteOutput(pcsz, len);
  136. }
  137.  
  138.  
  139. VOID WriteOutputW (PCWSTR pcwsz)
  140. {
  141.     CHAR buf[2048];
  142.     int len = WideCharToMultiByte(CP_ACP, 0, pcwsz, -1, buf, sizeof(buf)-1, NULL, NULL);
  143.     if (len > 0)
  144.     {
  145.         if (buf[len-1] == '\0')
  146.             len--;
  147.         else
  148.             buf[len] = '\0';
  149.  
  150.         if (g_fOutputIsConsole)
  151.             CharToOem(buf, buf);
  152.  
  153.         WriteOutput(buf, len);
  154.     }
  155. }
  156.  
  157.  
  158. VOID WriteOutputUtf8 (PCSTR pcszUtf8)
  159. {
  160.     PSTR pszAnsi;
  161.     if (Utf8ToAnsi(pcszUtf8, &pszAnsi))
  162.     {
  163.         if (!pszAnsi)
  164.         {
  165.             WriteOutput(pcszUtf8);
  166.         }
  167.         else
  168.         {
  169.             WriteOutput(pszAnsi);
  170.             delete pszAnsi;
  171.         }
  172.     }
  173. }
  174.  
  175.  
  176. VOID WriteOutputFmt (PCSTR fmt, ...)
  177. {
  178.     CHAR buf[WRITE_MAX_SIZE_NORM_FMT];
  179.     va_list va;
  180.     int len;
  181.  
  182.     va_start(va, fmt);
  183.     len = g_pJVIEW->vsnprintf(buf, sizeof(buf), fmt, va);
  184.     va_end(va);
  185.  
  186.     WriteOutput(buf, len);
  187. }
  188.  
  189.  
  190. VOID WriteOutputEx (DWORD flags, WriteOutputExArgs *pArgs, PCSTR pcsz)
  191. {
  192.     PSTR pszAnsi = NULL;
  193.  
  194.     if (flags & WRITE_UTF8)
  195.     {
  196.         if (!Utf8ToAnsi(pcsz, &pszAnsi))
  197.             return;
  198.  
  199.         if (pszAnsi)
  200.             pcsz = pszAnsi;
  201.  
  202.     }
  203.  
  204.     ULONG len = strlen(pcsz);
  205.  
  206.     if (   g_OutputWidth
  207.         && (flags & (WRITE_LEFT_JUSTIFIED | WRITE_CENTERED | WRITE_RIGHT_JUSTIFIED | WRITE_WRAP | WRITE_FIT)))
  208.     {
  209.         ULONG width = pArgs->Width;
  210.  
  211.         ULONG indent = width;
  212.  
  213.         if (flags & WRITE_FIT)
  214.         {
  215.             ULONG avail = g_OutputWidth - 1 - width;
  216.             indent = pArgs->FixedSpaceUsed + (ULONG)(pArgs->PctFreeSpaceUsed*avail/100);
  217.             width = (ULONG)(pArgs->PctFreeSpaceToUse*avail/100);
  218.             indent += width;
  219.         }
  220.  
  221.         if ((flags & WRITE_WRAP) && len > width)
  222.         {
  223.             WriteOutput(pcsz, len);
  224.             WriteOutput("\n", 1);
  225.  
  226.             while (indent--)
  227.                 WriteOutput(" ", 1);
  228.         }
  229.         else
  230.         {
  231.             ULONG spc;
  232.             if (width >= len)
  233.                 spc = width - len;
  234.             else
  235.                 spc = 0;
  236.  
  237.             ULONG before = 0;
  238.             ULONG after = spc;
  239.  
  240.             if (flags & WRITE_NEWLINE)
  241.                 after = 0;
  242.  
  243.             if (flags & WRITE_CENTERED)
  244.             {
  245.                 before = spc/2;
  246.                 if (!(flags & WRITE_NEWLINE))
  247.                     after = spc-before;
  248.             }
  249.             else if (flags & WRITE_RIGHT_JUSTIFIED)
  250.             {
  251.                 before = spc;
  252.                 after = 0;
  253.             }
  254.  
  255.             while (before--)
  256.                 WriteOutput(" ", 1);
  257.  
  258.             WriteOutput(pcsz, len);
  259.  
  260.             while (after--)
  261.                 WriteOutput(" ", 1);
  262.         }
  263.     }
  264.     else
  265.     {
  266.         WriteOutput(pcsz, len);
  267.     }
  268.  
  269.     if (pszAnsi)
  270.         delete pszAnsi;
  271.  
  272.     if (flags & WRITE_SPACE)
  273.         WriteOutput(" ", 1);
  274.     else if (flags & WRITE_NEWLINE)
  275.         WriteOutput("\n", 1);
  276. }
  277.  
  278.  
  279. VOID WriteOutputFmtEx (DWORD flags, WriteOutputExArgs *pArgs, PCSTR fmt, ...)
  280. {
  281.     CHAR buf[2048];
  282.     va_list va;
  283.  
  284.     va_start(va, fmt);
  285.  
  286.     g_pJVIEW->vsnprintf(buf, sizeof(buf), fmt, va);
  287.  
  288.     va_end(va);
  289.  
  290.     WriteOutputEx(flags, pArgs, buf);
  291. }
  292.  
  293.  
  294. VOID WriteOutputFitString (DWORD flags, ULONG FixedWidth, ULONG PctFreeSpaceToUse, PCSTR pcsz)
  295. {
  296.     WriteOutputExArgs args;
  297.  
  298.     args.Width = FixedWidth;
  299.     args.PctFreeSpaceToUse = PctFreeSpaceToUse;
  300.     args.PctFreeSpaceUsed = 0;
  301.     args.FixedSpaceUsed = 0;
  302.  
  303.     WriteOutputEx(flags | WRITE_FIT, &args, pcsz);
  304. }
  305.  
  306.  
  307. VOID WriteOutputColumnULONG (DWORD flags, ULONG ColumnWidth, ULONG value)
  308. {
  309.     CHAR buf[32];
  310.  
  311.     g_pJVIEW->snprintf(buf, sizeof(buf), "%u", value);
  312.  
  313.     WriteOutputColumnString(flags, ColumnWidth, buf);
  314. }
  315.  
  316.  
  317. VOID WriteOutputColumnString (DWORD flags, ULONG ColumnWidth, PCSTR pcsz)
  318. {
  319.     WriteOutputExArgs args;
  320.  
  321.     args.Width = ColumnWidth;
  322.  
  323.     WriteOutputEx(flags, &args, pcsz);
  324. }
  325.  
  326.  
  327. VOID WriteOutputColumnFmt (DWORD flags, ULONG ColumnWidth, PCSTR fmt, ...)
  328. {
  329.     CHAR buf[2048];
  330.     va_list va;
  331.  
  332.     va_start(va, fmt);
  333.  
  334.     g_pJVIEW->vsnprintf(buf, sizeof(buf), fmt, va);
  335.  
  336.     va_end(va);
  337.  
  338.     WriteOutputColumnString(flags, ColumnWidth, buf);
  339. }
  340.  
  341.  
  342. VOID WriteCurrentTime (PCSTR prefix, PCSTR suffix)
  343. {
  344.     SYSTEMTIME time;
  345.     GetLocalTime(&time);
  346.     WriteOutputFmt("%s%02u/%02u/%04u %02u:%02u:%02u%s",
  347.             prefix,
  348.             time.wMonth, time.wDay, time.wYear,
  349.             time.wHour, time.wMinute, time.wSecond,
  350.             suffix);
  351. }
  352.  
  353.  
  354. //------------------------------------------------------------------------
  355.  
  356.  
  357. STDMETHODIMP EventMonitor::JVIEWInitialize (IJVIEWProfilerUtils *putils)
  358. {
  359.     if (!InitTimer())
  360.         return E_FAIL;
  361.  
  362.     (g_pJVIEW = putils)->AddRef();
  363.     return S_OK;
  364. }
  365.  
  366.  
  367. struct ParamInfo
  368. {
  369.     PCSTR buf;
  370.     PCSTR opts;
  371.     BOOL failed;
  372. };
  373.  
  374. BOOL EnableCallOption (DWORD mask, ParamInfo *pinfo)
  375. {
  376.     if (pinfo->opts)
  377.     {
  378.         if ((g_ProfOptions & mask) != mask && (g_nMethodsToHook || g_nClassesToHook))
  379.         {
  380.             FatalError(IDS_MULTOPTSWITHCALLSPECS);
  381.             return FALSE;
  382.         }
  383.  
  384.         PCSTR p = pinfo->opts;
  385.  
  386.         if (*p != '\0')
  387.         {
  388.             for (;;)
  389.             {
  390.                 PCSTR pstart = p;
  391.                 PCSTR pmethsep = NULL;
  392.  
  393.                 BOOL fClass = TRUE;
  394.                 BOOL fParsing = FALSE;
  395.                 BOOL fWildcard = FALSE;
  396.  
  397.                 CHAR ch;
  398.  
  399.                 for (;;)
  400.                 {
  401.                     ch = *p;
  402.                     
  403.                     if (ch == ',' || ch == '+')
  404.                         break;
  405.  
  406.                     if (ch <= 32)
  407.                     {
  408.                         if (ch == '\0')
  409.                             break;
  410.                     
  411.                         if (fParsing)
  412.                         {
  413.                             FatalError(IDS_INVALIDMETHODSIG, p);
  414.                             return FALSE;
  415.                         }
  416.  
  417.                         pstart = p+1;
  418.                     }
  419.                     else
  420.                     {
  421.                         if (fParsing)
  422.                         {
  423.                             if (fWildcard)
  424.                             {
  425.                                 FatalError(IDS_NONTRAILINGWILDCARD, p-1);
  426.                                 return FALSE;
  427.                             }
  428.  
  429.                             if (ch == '.')
  430.                             {
  431.                                 if (fClass)
  432.                                 {
  433.                                     pmethsep = p;
  434.                                 }
  435.                             }
  436.                             else if (ch == '(')
  437.                             {
  438.                                 fClass = FALSE;
  439.                             }
  440.                             else if (ch == '*')
  441.                             {
  442.                                 fWildcard = TRUE;
  443.                             }
  444.                         }
  445.                         else
  446.                         {
  447.                             if (ch == '.' || ch == '(' || ch == ';' || ch == '/' || ch == ')')
  448.                             {
  449.                                 FatalError(IDS_INVALIDMETHODSIG, p);
  450.                                 return FALSE;
  451.                             }
  452.  
  453.                             if (ch == '*')
  454.                             {
  455.                                 fWildcard = TRUE;
  456.                             }
  457.                             
  458.                             fParsing = TRUE;
  459.                         }
  460.                     }
  461.  
  462.                     p++;
  463.                 }
  464.  
  465.                 PCSTR pend = p;
  466.  
  467.                 if (pstart < pend)
  468.                 {
  469.                     for (;;)
  470.                     {
  471.                         PSTR **ppArray;
  472.                         ULONG *pCount;
  473.  
  474.                         if (fClass)
  475.                         {
  476.                             ppArray = &g_rgpszClassesToHook;
  477.                             pCount = &g_nClassesToHook;
  478.                         }
  479.                         else
  480.                         {
  481.                             ppArray = &g_rgpszMethodsToHook;
  482.                             pCount = &g_nMethodsToHook;
  483.                         }
  484.  
  485.                         if (GrowStringArray(ppArray, pCount))
  486.                         {
  487.                             int len = pend - pstart;
  488.                             PSTR psz = new CHAR[len+1];
  489.                             CopyMemory(psz, pstart, len);
  490.                             psz[len] = '\0';
  491.  
  492.                             (*ppArray)[*pCount - 1] = psz;
  493.  
  494.                             if (fClass)
  495.                                 pmethsep = pend;
  496.  
  497.                             if (pmethsep)
  498.                             {
  499.                                 pmethsep = psz + (pmethsep - pstart);
  500.                                 
  501.                                 while (psz < pmethsep)
  502.                                 {
  503.                                     if (*psz == '.')
  504.                                         *psz = '/';
  505.                                     psz++;
  506.                                 }
  507.                             }
  508.                         }
  509.                         
  510.                         if (!fWildcard || !fClass)
  511.                             break;
  512.  
  513.                         fClass = FALSE;
  514.                         pmethsep = NULL;
  515.                     }
  516.                 }
  517.  
  518.                 if (ch == '\0')
  519.                     break;
  520.  
  521.                 p++;
  522.  
  523.                 if (ch == ',')
  524.                     break;
  525.             }
  526.         }
  527.  
  528.         pinfo->buf = p;
  529.     }
  530.  
  531.     g_ProfOptions |= (OPT_PROF_CALLS | mask);
  532.     return TRUE;
  533. }
  534.  
  535.  
  536. BOOL MatchParam (ParamInfo *pinfo, PCSTR param, int paramlen, BOOL fHasOptions)
  537. {
  538.     if (pinfo->failed)
  539.         return FALSE;
  540.  
  541.     PCSTR buf = pinfo->buf;
  542.  
  543.     if (memcmp(buf, param, paramlen) == 0)
  544.     {
  545.         PCSTR p = &buf[paramlen];
  546.  
  547.         for (;;)
  548.         {
  549.             CHAR endch = *p;
  550.  
  551.             if (endch == '\0')
  552.             {
  553.                 pinfo->buf = p;
  554.                 pinfo->opts = NULL;
  555.                 return TRUE;
  556.             }
  557.  
  558.             p++;
  559.  
  560.             if (endch == ',')
  561.             {
  562.                 pinfo->buf = p;
  563.                 pinfo->opts = NULL;
  564.                 return TRUE;
  565.             }
  566.  
  567.             if (endch <= 32)
  568.                 continue;
  569.  
  570.             if (endch == '=')
  571.             {
  572.                 if (!fHasOptions)
  573.                 {
  574.                     FatalError(IDS_NOSETTINGSFOROPT, param);
  575.                     pinfo->failed = TRUE;
  576.                     break;
  577.                 }
  578.                     
  579.                 pinfo->opts = p;
  580.                 return TRUE;
  581.             }
  582.         }
  583.     }
  584.  
  585.     return FALSE;
  586. }
  587.  
  588. #define MatchParam(pinfo,s,fHasOptions) MatchParam(pinfo, s, sizeof(s)-1, fHasOptions)
  589.  
  590.  
  591. STDMETHODIMP EventMonitor::ParseParameters (PCSTR pcszParams)
  592. {
  593.     const CHAR *p = pcszParams;
  594.  
  595.     ParamInfo info;
  596.     info.failed = FALSE;
  597.  
  598.     for (;;)
  599.     {
  600.         CHAR ch = *p++;
  601.  
  602.         if (ch == '\0')
  603.             break;
  604.  
  605.         if (ch <= 32)
  606.             continue;
  607.  
  608.         info.buf = p-1;
  609.  
  610.         if      (MatchParam(&info, "calls", TRUE)
  611.               || MatchParam(&info, "calltimes", TRUE))
  612.         {
  613.             if (!EnableCallOption(OPT_CALL_TIMES, &info))
  614.                 goto failed;
  615.         }
  616.         else if (MatchParam(&info, "calltrace", TRUE))
  617.         {
  618.             if (!EnableCallOption(OPT_CALL_TRACE, &info))
  619.                 goto failed;
  620.         }
  621.         else if (MatchParam(&info, "callparams", TRUE))
  622.         {
  623.             if (!EnableCallOption(OPT_CALL_TRACE | OPT_CALL_TRACE_PARAMS, &info))
  624.                 goto failed;
  625.         }
  626.         else if (MatchParam(&info, "permethodalloc", FALSE))
  627.         {
  628.             g_ProfOptions |= (OPT_PROF_ALLOC | OPT_ALLOC_PER_METHOD);
  629.         }
  630.         else if (MatchParam(&info, "perclassalloc", FALSE))
  631.         {
  632.             g_ProfOptions |= (OPT_PROF_ALLOC | OPT_ALLOC_PER_CLASS);
  633.         }
  634.         else if (MatchParam(&info, "gc", FALSE))
  635.         {
  636.             g_ProfOptions |= (OPT_PROF_GC | OPT_GC_SUMMARY);
  637.         }
  638.         else if (MatchParam(&info, "heapdump", FALSE))
  639.         {
  640.             g_ProfOptions |= (OPT_PROF_GC | OPT_DUMP_HEAP);
  641.         }
  642.         else if (MatchParam(&info, "verbose", FALSE))
  643.         {
  644.             g_ProfOptions |= OPT_VERBOSE;
  645.         }
  646.         else if (MatchParam(&info, "file", TRUE))
  647.         {
  648.             if (!info.opts)
  649.             {
  650.                 FatalError(IDS_MISSINGFILENAME);
  651.                 goto failed;
  652.             }
  653.  
  654.             PCSTR comma = info.opts;
  655.             while (*comma && *++comma != ',');
  656.             PCSTR sep = comma;
  657.  
  658.             int len = sep - info.opts;
  659.  
  660.             g_pszOutputFileName = new CHAR[len+1];
  661.             if (!g_pszOutputFileName)
  662.                 goto failed;
  663.  
  664.             CopyMemory(g_pszOutputFileName, info.opts, len);
  665.             g_pszOutputFileName[len] = '\0';
  666.  
  667.             if (*comma)
  668.                 sep++;
  669.             else
  670.                 break;
  671.             info.buf = sep;
  672.         }
  673.         else if (MatchParam(&info, "sample", TRUE))
  674.         {
  675.             g_ProfOptions |= OPT_SAMPLE;
  676.  
  677.             if (info.opts)
  678.             {
  679.                 if (g_fSamplingFrequencySpecified)
  680.                 {
  681.                     FatalError(IDS_MULTSAMPFREQ, info.opts);
  682.                         goto failed;
  683.                 }
  684.  
  685.                 g_SamplingFrequency = atoi(info.opts);
  686.                 if (g_SamplingFrequency == 0)
  687.                 {
  688.                     FatalError(IDS_INVALIDSAMPFREQ, info.opts);
  689.                         goto failed;
  690.                 }
  691.  
  692.                 g_fSamplingFrequencySpecified = TRUE;
  693.  
  694.                 PCSTR comma = info.opts;
  695.                 while (*comma && *++comma != ',');
  696.                 if (*comma)
  697.                     info.buf = comma+1;
  698.                 else
  699.                     break;
  700.             }
  701.         }
  702.         else if (MatchParam(&info, "table", FALSE))
  703.         {
  704.             g_ProfOptions |= OPT_TABLE;
  705.         }
  706.         else if (   MatchParam(&info, "?", FALSE)
  707.                  || MatchParam(&info, "help", FALSE))
  708.         {
  709.             DisplayUsage();
  710.  
  711.             return E_FAIL;
  712.         }
  713.         else
  714.         {
  715.             if (info.failed)
  716.                 goto failed;
  717.  
  718.             FatalError(IDS_INVALIDOPTION, info.buf);
  719.             goto failed;
  720.         }
  721.  
  722.         p = info.buf;
  723.     }
  724.  
  725.     if ((g_ProfOptions & (OPT_PROF_CALLS | OPT_SAMPLE)) == (OPT_PROF_CALLS | OPT_SAMPLE))
  726.     {
  727.         FatalError(IDS_DONTSAMPLEANDHOOK);
  728.  
  729. failed:
  730.         return E_FAIL;
  731.     }
  732.  
  733.     return S_OK;
  734. }
  735.  
  736.  
  737. //------------------------------------------------------------------------
  738.  
  739.  
  740. VOID EventMonitor::OnVMInitialization ()
  741. {
  742.     m_fVMInitialized = TRUE;
  743.  
  744.     HMODULE hmodMSJava = GetModuleHandle("MSJAVA.DLL");
  745.     if (hmodMSJava)
  746.     {
  747.         typedef BOOL __cdecl AddModuleResourceClassSourceProc (HMODULE hMod, DWORD dwResID);
  748.         AddModuleResourceClassSourceProc *pfnAddModuleResourceClassSource;
  749.  
  750.         pfnAddModuleResourceClassSource = (AddModuleResourceClassSourceProc*)GetProcAddress(hmodMSJava, "AddModuleResourceClassSource");
  751.         if (pfnAddModuleResourceClassSource)
  752.         {
  753.             (*pfnAddModuleResourceClassSource)(g_hInst, JEX_DEFAULT_CLASS_RESOURCE_ID);
  754.         }
  755.     }
  756.  
  757.     m_sampler.OnVMInitialization();
  758. }
  759.  
  760.  
  761. //------------------------------------------------------------------------
  762.  
  763.  
  764. ThreadRecord *EventMonitor::CreateCurrentThreadRecord ()
  765. {
  766.     ThreadID vmid = NULL;
  767.     DWORD tid = GetCurrentThreadId();
  768.  
  769.     if (m_monitor_info2)
  770.         m_monitor_info2->GetCurrentThread(&vmid);
  771.  
  772.     ThreadRecord *pThread = GetThreadRecord(vmid, tid);
  773.     if (pThread)
  774.         TlsSetValue(m_threadtls, pThread);
  775.  
  776.     return pThread;
  777. }
  778.  
  779.  
  780. ThreadRecord *EventMonitor::GetThreadRecord (ThreadID vmid, DWORD tid)
  781. {
  782.     ThreadRecord *ret = NULL;
  783.  
  784.     EnterCriticalSection(&m_ThreadEventCS);
  785.     {
  786.         for (ULONG i = 0; i < m_nthreads; i++)
  787.         {
  788.             ThreadRecord *prec = m_rgpthreads[i];
  789.             if (prec->tid == tid)
  790.             {
  791.                 prec->vmid = vmid;
  792.                 ret = prec;
  793.                 break;
  794.             }
  795.         }
  796.  
  797.         ret = CreateThreadRecord(vmid, tid);
  798.     }
  799.     LeaveCriticalSection(&m_ThreadEventCS);
  800.  
  801.     return ret;
  802. }
  803.  
  804.  
  805. ThreadRecord *EventMonitor::CreateThreadRecord (ThreadID vmid, DWORD tid)
  806. {
  807.     ThreadRecord *pThread = NULL;
  808.  
  809.     if (m_nthreads >= m_maxthreads)
  810.     {
  811.         GrowPtrArray((PVOID**)&m_rgpthreads, &m_maxthreads, (m_maxthreads+1)*2);
  812.     }
  813.  
  814.     if (m_nthreads < m_maxthreads)
  815.     {
  816.         pThread = new ThreadRecord();
  817.         if (pThread)
  818.         {
  819.             ZeroMemory(pThread, sizeof(*pThread));
  820.         
  821.             pThread->vmid = vmid;
  822.             pThread->tid = tid;
  823.  
  824.             m_rgpthreads[m_nthreads++] = pThread;
  825.         }
  826.     }
  827.  
  828.     return pThread;
  829. }
  830.  
  831.  
  832. //------------------------------------------------------------------------
  833.  
  834.  
  835. EventMonitor::EventMonitor ()
  836. {
  837.     m_refcount = 1;
  838.  
  839.     m_monitor_info = NULL;
  840.     m_monitor_info2 = NULL;
  841.     m_monitor_info3 = NULL;
  842.     m_monitor_info4 = NULL;
  843.  
  844.     m_VMRelease = VM_CURRENT;
  845.  
  846.     m_fVMInitialized = FALSE;
  847.     m_fVMTerminated = FALSE;
  848.  
  849.     m_threadtls = TLS_OUT_OF_INDEXES;
  850.     m_rgpthreads = NULL;
  851.     m_nthreads = 0;
  852.     m_maxthreads = 0;
  853.     InitializeCriticalSection(&m_ThreadEventCS);
  854. }
  855.  
  856.  
  857. EventMonitor::~EventMonitor(void)
  858. {
  859.     g_EventMonitor = NULL;
  860.  
  861.     m_callprof.Destruct();
  862.     m_sampler.Destruct();
  863.     m_calltracer.Destruct();
  864.     m_allocprof.Destruct();
  865.     m_gcprof.Destruct();
  866.  
  867.     if (m_monitor_info4)
  868.         m_monitor_info4->Release();
  869.  
  870.     if (m_monitor_info3)
  871.         m_monitor_info3->Release();
  872.  
  873.     if (m_monitor_info2)
  874.         m_monitor_info2->Release();
  875.  
  876.     if (m_monitor_info)
  877.         m_monitor_info->Release();
  878.  
  879.     if (m_rgpthreads)
  880.         delete(m_rgpthreads);
  881.     DeleteCriticalSection(&m_ThreadEventCS);
  882.     if (m_threadtls != TLS_OUT_OF_INDEXES)
  883.         TlsFree(m_threadtls);
  884. }
  885.  
  886.  
  887. VOID EventMonitor::ShutdownWorker (BOOL fNormal)
  888. {
  889.     if (!g_EventMonitor)
  890.         return;
  891.  
  892.     g_EventMonitor = NULL;
  893.     
  894.     if (!fNormal)
  895.         WriteOutput("Process terminated.\n");
  896.  
  897.     WriteCurrentTime("End time: ", "\n");
  898.  
  899.     m_fVMTerminated = TRUE;
  900.  
  901.     m_callprof.SpewResults();
  902.     m_allocprof.SpewResults();
  903.     m_sampler.SpewSamplerResults();
  904.  
  905.     WriteOutput("--- End of profiler output ---\n");
  906.  
  907.     CloseHandle(g_hOutput);
  908.     g_hOutput = INVALID_HANDLE_VALUE;
  909. }
  910.  
  911.  
  912. //------------------------------------------------------------------------
  913.  
  914.  
  915. ULONG EventMonitor::AddRef ()
  916. {
  917.     return (ULONG)InterlockedIncrement((LPLONG)&m_refcount);
  918. }
  919.  
  920.  
  921. ULONG EventMonitor::Release ()
  922. {
  923.     ULONG refcount = (ULONG)InterlockedDecrement((LPLONG)&m_refcount);
  924.     if (!refcount)
  925.         delete this;
  926.     return refcount;
  927. }
  928.  
  929.  
  930. STDMETHODIMP EventMonitor::QueryInterface (REFIID riid, void ** ppv)
  931. {
  932.     if (   riid == IID_IJavaEventMonitor
  933.         || riid == IID_IJavaEventMonitor2
  934.         || riid == IID_IUnknown)
  935.     {
  936.         *ppv = (IJavaEventMonitor2*)this;
  937.     }
  938.     else if (riid == IID_IObjectAllocationCallback)
  939.     {
  940.         *ppv = (IObjectAllocationCallback*)this;
  941.     }
  942.     else if (riid == IID_IJVIEWProfiler)
  943.     {
  944.         *ppv = (IJVIEWProfiler*)this;
  945.     }
  946.     else
  947.     {
  948.         *ppv = NULL;
  949.         return E_NOINTERFACE;
  950.     }
  951.  
  952.     AddRef();
  953.     return S_OK;
  954. }
  955.  
  956.  
  957. //------------------------------------------------------------------------
  958.  
  959.  
  960. STDMETHODIMP EventMonitor::Initialize(LPCSTR pclass_file_name, IJavaEventMonitorIDInfo *pmonitor_info, DWORD java_flags, DWORD *prequested_events)
  961. {
  962.     g_fErrorMessageDisplayed = FALSE;
  963.  
  964.     HRESULT hr = S_OK;
  965.  
  966.     (m_monitor_info = pmonitor_info)->AddRef();
  967.  
  968.     if (m_monitor_info->QueryInterface(IID_IJavaEventMonitorIDInfo4, (void **)&m_monitor_info4) != S_OK)
  969.     {
  970.         m_monitor_info4 = NULL;
  971.         m_VMRelease = VM_SDK31;
  972.     }
  973.  
  974.     if (m_monitor_info->QueryInterface(IID_IJavaEventMonitorIDInfo3, (void **)&m_monitor_info3) != S_OK)
  975.     {
  976.         m_monitor_info3 = NULL;
  977.         m_VMRelease = VM_SDK30PR1;
  978.     }
  979.  
  980.     if (m_monitor_info->QueryInterface(IID_IJavaEventMonitorIDInfo2, (void **)&m_monitor_info2) != S_OK)
  981.     {
  982.         m_monitor_info2 = NULL;
  983.         m_VMRelease = VM_IE40;
  984.     }
  985.  
  986.     // These are the characteristics of the IE 4 VM, the first VM that
  987.     // supported profiling.  It did not support GetProfilingCapabilities.
  988.  
  989.     DWORD SupportedStates = (  JVM_STATE_INTERPRETER_ENABLED
  990. #ifdef _X86_
  991.                              | JVM_STATE_FAST_INTERPRETER_ENABLED
  992.                              | JVM_STATE_JIT_COMPILER_ENABLED
  993. #endif
  994.                              | JVM_STATE_DEBUGGER_ENABLED);
  995.  
  996.     // Note that JVM_MONITOR_SOURCE_LINE_EXECUTION,
  997.     // JVM_MONITOR_MONITOR_OPERATIONS, and JVM_MONITOR_THREADS were defined in
  998.     // the original IE4 jevmon.idl, but they were not fully supported by that
  999.     // VM.
  1000.  
  1001.     DWORD SupportedEventCategories = (  JVM_MONITOR_CLASS_LOADS
  1002.                                       | JVM_MONITOR_METHOD_CALLS
  1003.                                       | JVM_MONITOR_JIT_COMPILATION
  1004.                                       | JVM_MONITOR_BYTE_CODE_EXECUTION
  1005.                                       | JVM_MONITOR_EXCEPTIONS
  1006.                                       | JVM_MONITOR_GARBAGE_COLLECTIONS);
  1007.  
  1008.     JAVA_EXECUTION_MODEL LastSupportedExecModel = JVM_EXECUTION_COM;
  1009.  
  1010.     JVM_EVENT_TYPE LastSupportedEventType = JVM_EVENT_TYPE_SHUTDOWN;
  1011.  
  1012.     // IJavaEventMonitor2 was not supported by the IE4 VM.
  1013.     JVM_EVENT_TYPE2 LastSupportedEventType2 = (JVM_EVENT_TYPE2)-1;
  1014.  
  1015.     if (m_monitor_info2 != NULL)
  1016.     {
  1017.         LPSTR pszClassPath;
  1018.         hr = m_monitor_info2->GetClassPath(&pszClassPath);
  1019.         if (hr == S_OK)
  1020.         {
  1021.             WriteOutputFmt("class path: %s\n", pszClassPath);
  1022.  
  1023.             CoTaskMemFree(pszClassPath);
  1024.         }
  1025.  
  1026.         hr = m_monitor_info2->GetProfilingCapabilities(
  1027.             &SupportedStates,
  1028.             &SupportedEventCategories,
  1029.             &LastSupportedExecModel,
  1030.             &LastSupportedEventType,
  1031.             &LastSupportedEventType2);
  1032.     }
  1033.  
  1034.  
  1035.     if (!(g_ProfOptions & ~OPT_NON_EVENT_OPTIONS))
  1036.     {
  1037.         g_ProfOptions |= OPT_DEFAULT_OPTIONS;
  1038.     }
  1039.  
  1040.  
  1041.     if (hr == S_OK)
  1042.     {
  1043.         if (g_pszOutputFileName)
  1044.         {
  1045.             g_hOutput = CreateFile(
  1046.                     g_pszOutputFileName,
  1047.                     GENERIC_WRITE,
  1048.                     FILE_SHARE_READ,
  1049.                     NULL,
  1050.                     OPEN_ALWAYS,
  1051.                     0,
  1052.                     NULL);
  1053.             if (g_hOutput == INVALID_HANDLE_VALUE)
  1054.             {
  1055.                 FatalError(IDS_CANTAPPEND, g_pszOutputFileName);
  1056.                 hr = HRESULT_FROM_WIN32(GetLastError());
  1057.             }
  1058.             else
  1059.             {
  1060.                 LONG dwSizeHi = 0;
  1061.                 LONG dwSizeLo = SetFilePointer(g_hOutput, 0, &dwSizeHi, FILE_END);
  1062.             }
  1063.  
  1064.             if (g_ProfOptions & OPT_TABLE)
  1065.             {
  1066.                 g_OutputWidth = 0;
  1067.             }
  1068.             else
  1069.             {
  1070.                 g_OutputWidth = 120;
  1071.             }
  1072.         }
  1073.         else
  1074.         {
  1075.             g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  1076.  
  1077.             if (g_ProfOptions & OPT_TABLE)
  1078.             {
  1079.                 g_OutputWidth = 0;
  1080.             }
  1081.             else
  1082.             {
  1083.                 CONSOLE_SCREEN_BUFFER_INFO coninfo;
  1084.                 if (GetConsoleScreenBufferInfo(g_hOutput, &coninfo))
  1085.                 {
  1086.                     g_OutputWidth = coninfo.dwSize.X;
  1087.                     g_fOutputIsConsole = TRUE;
  1088.                 }
  1089.                 else
  1090.                 {
  1091.                     g_OutputWidth = 80;
  1092.                 }
  1093.             }
  1094.         }
  1095.     }
  1096.  
  1097.  
  1098.     if (hr == S_OK)
  1099.     {
  1100.         BOOL first = TRUE;
  1101.  
  1102.         WriteOutput("VM state:");
  1103.  
  1104. #define SPEWSTATE(flag,descr) \
  1105.         if ((java_flags & (flag)) != 0) \
  1106.         { \
  1107.             if (!first) \
  1108.                 WriteOutput(","); \
  1109.             else \
  1110.                 first = FALSE; \
  1111.             WriteOutput(" " descr); \
  1112.         }
  1113.  
  1114.         SPEWSTATE(JVM_STATE_INTERPRETER_ENABLED, "Interpreter loop");
  1115.         SPEWSTATE(JVM_STATE_FAST_INTERPRETER_ENABLED, "Fast interpreter loop");
  1116.         SPEWSTATE(JVM_STATE_JIT_COMPILER_ENABLED, "JIT compiler");
  1117.         SPEWSTATE(JVM_STATE_DEBUGGER_ENABLED, "Debugging");
  1118.  
  1119. #undef SPEWSTATE
  1120.  
  1121.         WriteOutput("\n");
  1122.     }
  1123.  
  1124.  
  1125.     if (hr == S_OK)
  1126.     {
  1127.         hr = m_gcprof.Initialize(this);
  1128.     }
  1129.  
  1130.     if (hr == S_OK)
  1131.     {
  1132.         hr = m_callprof.Initialize(this);
  1133.     }
  1134.  
  1135.     if (hr == S_OK)
  1136.     {
  1137.         hr = m_calltracer.Initialize(this);
  1138.     }
  1139.  
  1140.     if (hr == S_OK)
  1141.     {
  1142.         hr = m_allocprof.Initialize(this);
  1143.     }
  1144.  
  1145.     if (hr == S_OK)
  1146.     {
  1147.         hr = m_sampler.Initialize(this, &m_callprof);
  1148.     }
  1149.  
  1150.  
  1151.     // Install an allocation hook if any form of allocation profiling is enabled.
  1152.  
  1153.     if (hr == S_OK)
  1154.     {
  1155.         if (g_ProfOptions & OPT_PROF_ALLOC)
  1156.         {
  1157.             UINT idError = IDS_CANTHOOKALLOCS;
  1158.  
  1159.             IJavaHeapMonitor *pheapmon;
  1160.             hr = pmonitor_info->QueryInterface(IID_IJavaHeapMonitor, (PVOID*)&pheapmon);
  1161.             if (hr == S_OK)
  1162.             {
  1163.                 IObjectAllocationCallback *alloccb;
  1164.                 hr = QueryInterface(IID_IObjectAllocationCallback, (PVOID*)&alloccb);
  1165.                 if (hr == S_OK)
  1166.                 {
  1167.                     hr = pheapmon->NotifyOnObjectAllocations(alloccb);
  1168.  
  1169.                     alloccb->Release();
  1170.                 }
  1171.             }
  1172.             else
  1173.             {
  1174.                 idError = IDS_ALLOCHOOKNOTSUP;
  1175.             }
  1176.  
  1177.             if (hr != S_OK)
  1178.             {
  1179.                 FatalError(idError);
  1180.             }
  1181.         }
  1182.     }
  1183.  
  1184.  
  1185.     // Determine which events are needed based on the specified profiling options.
  1186.  
  1187.     if (hr == S_OK)
  1188.     {
  1189.         DWORD events = 0;
  1190.  
  1191.         if (g_ProfOptions & OPT_PROF_CALLS)
  1192.         {
  1193.             if (LastSupportedEventType2 < JVM_EVENT_TYPE2_EXCEPTION_THROWN)
  1194.             {
  1195.                 // TODO: The call profiling implementation here depends in
  1196.                 // IJavaEventMonitor2::MethodExit2 and
  1197.                 // JVM_EVENT_TYPE2_EXCEPTION_THROWN.  To implement this for the
  1198.                 // IE4 VM, the stack must be mirrored to know the method that
  1199.                 // each call is returning to.  MethodExit only provides the
  1200.                 // StackID.
  1201.  
  1202.                 FatalError(IDS_CALLPROFNOTIMPFORIE4VM);
  1203.                 hr = E_FAIL;
  1204.             }
  1205.  
  1206.             if (hr == S_OK)
  1207.             {
  1208.                 if (g_nClassesToHook + g_nMethodsToHook)
  1209.                 {
  1210.                     events |= (JVM_MONITOR_CLASS_LOADS | JVM_MONITOR_SPECIFIC_METHOD_CALLS);
  1211.  
  1212.                     if (!(SupportedEventCategories & JVM_MONITOR_SPECIFIC_METHOD_CALLS) || !m_monitor_info3)
  1213.                     {
  1214.                         FatalError(IDS_SPECMETHODSNOTSUP);
  1215.                         hr = E_FAIL;
  1216.                     }
  1217.                 }
  1218.                 else
  1219.                 {
  1220.                     events |= JVM_MONITOR_METHOD_CALLS;
  1221.                 }
  1222.  
  1223.                 events |= (JVM_MONITOR_THREADS | JVM_MONITOR_EXCEPTIONS);
  1224.             }
  1225.         }
  1226.  
  1227.         if (hr == S_OK)
  1228.         {
  1229.             if (g_ProfOptions & OPT_PROF_ALLOC)
  1230.             {
  1231.                 events |= JVM_MONITOR_GARBAGE_COLLECTIONS;
  1232.  
  1233.                 if (g_ProfOptions & OPT_ALLOC_PER_METHOD)
  1234.                 {
  1235.                     if (LastSupportedEventType2 < JVM_EVENT_TYPE2_STACK_TRACE)
  1236.                     {
  1237.                         FatalError(IDS_STACKTRACENOTSUP);
  1238.                         hr = E_FAIL;
  1239.                     }
  1240.  
  1241.                     events |= JVM_MONITOR_THREADS;
  1242.                 }
  1243.             }
  1244.         }
  1245.  
  1246.         if (hr == S_OK)
  1247.         {
  1248.             if (g_ProfOptions & OPT_PROF_GC)
  1249.             {
  1250.                 events |= JVM_MONITOR_GARBAGE_COLLECTIONS;
  1251.             }
  1252.         }
  1253.  
  1254.         if (hr == S_OK)
  1255.         {
  1256.             if (g_ProfOptions & OPT_SAMPLE)
  1257.             {
  1258.                 if (!(SupportedEventCategories & JVM_MONITOR_SAMPLING))
  1259.                 {
  1260.                     FatalError(IDS_SAMPLINGNOTSUP);
  1261.                     hr = E_FAIL;
  1262.                 }
  1263.  
  1264.                 events |= (JVM_MONITOR_THREADS | JVM_MONITOR_SAMPLING);
  1265.  
  1266.                 if (LastSupportedEventType2 < JVM_EVENT_TYPE2_INITIALIZED)
  1267.                 {
  1268.                     // If this event is not supported, then presumably
  1269.                     // IJavaEventMonitorIDInfo2::SampleThreadLocation will
  1270.                     // simply fail until the VM is fully initialized.
  1271.  
  1272.                     OnVMInitialization();
  1273.                 }
  1274.  
  1275.                 // We won't bother to check for JVM_EVENT_TYPE_SHUTDOWN - if
  1276.                 // it's not supported, VM apis must already be safe to call
  1277.                 // during shutdown.  Also, the VM cannot reliably dispatch
  1278.                 // this event, so shutdown code must already assume that this
  1279.                 // event may never be received.
  1280.             }
  1281.         }
  1282.  
  1283.  
  1284.         if (g_ProfOptions & OPT_VERBOSE)
  1285.         {
  1286.             events |=   JVM_MONITOR_CLASS_LOADS
  1287.                       | JVM_MONITOR_EXCEPTIONS
  1288.                       | JVM_MONITOR_GARBAGE_COLLECTIONS
  1289.                       | JVM_MONITOR_THREADS;
  1290.  
  1291.             events &= SupportedEventCategories;
  1292.         }
  1293.  
  1294.  
  1295.         if (hr == S_OK && (SupportedEventCategories & events) != events)
  1296.         {
  1297.             FatalError(IDS_OPTNOTSUP);
  1298.             hr = E_FAIL;
  1299.         }
  1300.  
  1301.  
  1302.         if (hr == S_OK)
  1303.         {
  1304.             if (events & JVM_MONITOR_THREADS)
  1305.             {
  1306.                 m_threadtls = TlsAlloc();
  1307.                 if (m_threadtls == TLS_OUT_OF_INDEXES)
  1308.                     hr = E_OUTOFMEMORY;
  1309.             }
  1310.         }
  1311.  
  1312.         if (hr == S_OK)
  1313.         {
  1314.             *prequested_events = events;
  1315.  
  1316.             if (m_monitor_info4)
  1317.             {
  1318.                 // Enabling these two options allows the VM to substantially
  1319.                 // reduce method call event overhead.
  1320.  
  1321.                 DWORD initopts =   JVM_INIT_OPT_GC_SAFE_METHOD_CALLS
  1322.                                  | JVM_INIT_OPT_WONT_TOGGLE_METHOD_CALL_EVENTS;
  1323.  
  1324.                 // Enabling these two removes slightly more.
  1325.  
  1326.                 initopts |= JVM_INIT_OPT_FP_SAFE_METHOD_CALLS;
  1327.                                  
  1328.                 if (!(g_ProfOptions & OPT_CALL_TRACE_PARAMS))
  1329.                     initopts |= JVM_INIT_OPT_RETURN_VALUE_NOT_NEEDED;
  1330.  
  1331.                 m_monitor_info4->SetMonitorInitializationOptions(initopts);
  1332.             }
  1333.         }
  1334.     }
  1335.  
  1336.  
  1337.     if (g_ProfOptions & OPT_VERBOSE)
  1338.     {
  1339.         // TODO: spew event mask
  1340.     }
  1341.  
  1342.  
  1343.     if (hr == S_OK)
  1344.     {
  1345.         g_EventMonitor = this;
  1346.     
  1347.         WriteCurrentTime("Start time: ", "\n");
  1348.     }
  1349.     else
  1350.     {
  1351.         // Returning a non-S_OK value detaches the profiler.  If some path
  1352.         // through here didn't indicate a failure to the user, this is our
  1353.         // last chance to do so.
  1354.  
  1355.         if (!g_fErrorMessageDisplayed)
  1356.             FatalError(IDS_CANTPROFILE);
  1357.     }
  1358.  
  1359.  
  1360.     return(hr);
  1361. }
  1362.  
  1363.  
  1364. STDMETHODIMP EventMonitor::NotifyEvent(JVM_EVENT_TYPE event, UniqueID event_id)
  1365. {
  1366.     HRESULT hr;
  1367.  
  1368.     switch (event)
  1369.     {
  1370.         ThreadRecord *pThread;
  1371.     
  1372.         case JVM_EVENT_TYPE_THREAD_CREATE:          // A Java threadID is being created (ThreadID created)
  1373.  
  1374.             pThread = NULL;
  1375.  
  1376.             if (m_monitor_info2)
  1377.             {
  1378.                 DWORD tid = 0;
  1379.                 if (SUCCEEDED(m_monitor_info2->ThreadInformation((ThreadID)event_id, &tid)))
  1380.                     pThread = GetThreadRecord((ThreadID)event_id, tid);
  1381.             }
  1382.  
  1383.             if (g_ProfOptions & OPT_VERBOSE)
  1384.             {
  1385.                 if (pThread)
  1386.                     WriteOutputFmt("Thread %x created\n", pThread->tid);
  1387.                 else
  1388.                     WriteOutput("Thread created\n");
  1389.             }
  1390.  
  1391.             break;
  1392.  
  1393.         case JVM_EVENT_TYPE_THREAD_DESTROY:         // A Java threadID is being destroyed (ThreadID destroyed)
  1394.  
  1395.             pThread = NULL;
  1396.  
  1397.             EnterCriticalSection(&m_ThreadEventCS);
  1398.             {
  1399.                 ThreadID vmid = (ThreadID)event_id;
  1400.                 DWORD tid = 0;
  1401.  
  1402.                 if (m_monitor_info2)
  1403.                     m_monitor_info2->ThreadInformation(vmid, &tid);
  1404.             
  1405.                 if (m_rgpthreads)
  1406.                 {
  1407.                     unsigned i;
  1408.                     for (i = 0; i < m_nthreads; i++)
  1409.                     {
  1410.                         pThread = m_rgpthreads[i];
  1411.                         if (pThread->vmid == vmid || pThread->tid == tid)
  1412.                         {
  1413.                             m_nthreads--;
  1414.                             m_rgpthreads[i] = m_rgpthreads[m_nthreads];
  1415.                             break;
  1416.                         }
  1417.                     }
  1418.                 }
  1419.             }
  1420.             LeaveCriticalSection(&m_ThreadEventCS);
  1421.  
  1422.             if (g_ProfOptions & OPT_VERBOSE)
  1423.             {
  1424.                 if (pThread)
  1425.                     WriteOutputFmt("Thread %x destroyed\n", pThread->tid);
  1426.                 else
  1427.                     WriteOutput("Thread destroyed\n");
  1428.             }
  1429.  
  1430.             if (pThread)
  1431.                 delete pThread;
  1432.  
  1433.             break;
  1434.  
  1435.         case JVM_EVENT_TYPE_CLASS_LOAD_STARTED:     // A class is being loaded (PSTR)
  1436.             if (g_ProfOptions & OPT_VERBOSE)
  1437.                 WriteOutputFmt("Loading class %s...\n", (PCSTR)event_id);
  1438.             break;
  1439.  
  1440.         case JVM_EVENT_TYPE_CLASS_LOAD_FAILED:      // A class failed to load (PSTR).
  1441.             if (g_ProfOptions & OPT_VERBOSE)
  1442.                 WriteOutputFmt("Failed to load class %s.\n", (PCSTR)event_id);
  1443.             break;
  1444.  
  1445.         case JVM_EVENT_TYPE_CLASS_LOAD_FINISHED:    // A class has been loaded (ClassID)
  1446.  
  1447.             if (m_VMRelease > VM_IE40)
  1448.             {
  1449.                 PSTR pszClassUtf8 = NULL;
  1450.                 int ncMethods;
  1451.                 MethodID *pmethodid = NULL;
  1452.  
  1453.                 BOOL fNeedClassName = (g_ProfOptions & OPT_VERBOSE) || g_nClassesToHook;
  1454.  
  1455.                 hr = m_monitor_info->ClassInformation(
  1456.                         (ClassID)event_id,
  1457.                         fNeedClassName ? &pszClassUtf8 : NULL,
  1458.                         NULL,
  1459.                         &ncMethods,
  1460.                         g_nMethodsToHook ? &pmethodid : NULL,
  1461.                         NULL);
  1462.  
  1463.                 if (SUCCEEDED(hr))
  1464.                 {
  1465.                     if (pszClassUtf8)
  1466.                     {
  1467.                         PSTR pszClass = pszClassUtf8;
  1468.                         PSTR pszClassAnsi;
  1469.                         if (Utf8ToAnsi(pszClassUtf8, &pszClassAnsi))
  1470.                         {
  1471.                             if (pszClassAnsi)
  1472.                                 pszClass = pszClassAnsi;
  1473.                                 
  1474.                             if (g_ProfOptions & OPT_VERBOSE)
  1475.                                 WriteOutputFmt("Class %s successfully loaded.\n", pszClass);
  1476.  
  1477.                             if (g_nClassesToHook)
  1478.                             {
  1479.                                 ULONG iPattern;
  1480.  
  1481.                                 for (iPattern = 0; iPattern < g_nClassesToHook; iPattern++)
  1482.                                 {
  1483.                                     if (match(g_rgpszClassesToHook[iPattern], pszClass))
  1484.                                     {
  1485.                                         m_monitor_info3->EnableMethodCallEvents(
  1486.                                                 (ClassID)event_id,
  1487.                                                 JVM_ID_CLASS,
  1488.                                                 JVM_PERMIT_METHOD_CALL_EVENTS);
  1489.                                         ncMethods = 0;
  1490.                                         break;
  1491.                                     }
  1492.                                 }
  1493.                             }
  1494.  
  1495.                             if (pszClassAnsi)
  1496.                                 delete pszClassAnsi;
  1497.                         }
  1498.  
  1499.                         CoTaskMemFree(pszClassUtf8);
  1500.                     }
  1501.  
  1502.                     if (pmethodid)
  1503.                     {
  1504.                         int iMethod;
  1505.  
  1506.                         for (iMethod = 0; iMethod < ncMethods; iMethod++)
  1507.                         {
  1508.                             PSTR pszMethodUtf8;
  1509.                             if (SUCCEEDED(m_monitor_info->MethodInformation(pmethodid[iMethod], &pszMethodUtf8, NULL, NULL, NULL, NULL)))
  1510.                             {
  1511.                                 PSTR pszMethod = pszMethodUtf8;
  1512.                                 PSTR pszMethodAnsi;
  1513.                                 if (Utf8ToAnsi(pszMethodUtf8, &pszMethodAnsi))
  1514.                                 {
  1515.                                     if (pszMethodAnsi)
  1516.                                         pszMethod = pszMethodAnsi;
  1517.  
  1518.                                     ULONG iPattern;
  1519.  
  1520.                                     for (iPattern = 0; iPattern < g_nMethodsToHook; iPattern++)
  1521.                                     {
  1522.                                         if (match(g_rgpszMethodsToHook[iPattern], pszMethod))
  1523.                                         {
  1524.                                             m_monitor_info3->EnableMethodCallEvents(
  1525.                                                     pmethodid[iMethod],
  1526.                                                     JVM_ID_METHOD,
  1527.                                                     JVM_PERMIT_METHOD_CALL_EVENTS);
  1528.                                             break;
  1529.                                         }
  1530.                                     }
  1531.  
  1532.                                     if (pszMethodAnsi)
  1533.                                         delete pszMethodAnsi;
  1534.                                 }
  1535.  
  1536.                                 CoTaskMemFree(pszMethodUtf8);
  1537.                             }
  1538.                         }
  1539.  
  1540.                         CoTaskMemFree(pmethodid);
  1541.                     }
  1542.                 }
  1543.             }
  1544.             else
  1545.             {
  1546.                 // Sorry.  The IE4 VM provided a PCSTR instead of a ClassID.
  1547.  
  1548.                 if (g_ProfOptions & OPT_VERBOSE)
  1549.                     WriteOutputFmt("Class %s successfully loaded.\n", (PCSTR)event_id);
  1550.             }
  1551.             
  1552.             break;
  1553.  
  1554.         case JVM_EVENT_TYPE_CLASS_UNLOAD:           // A class has been unloaded (ClassID)
  1555.         {
  1556.             if (m_VMRelease > VM_IE40)
  1557.             {
  1558.                 // BUGBUG: remove any references to the unloading class, cache any info needed
  1559.  
  1560.                 PSTR pszClassUtf8;
  1561.  
  1562.                 hr = m_monitor_info->ClassInformation(event_id, &pszClassUtf8, NULL, NULL, NULL, NULL);
  1563.  
  1564.                 if (hr == S_OK)
  1565.                 {
  1566.                     if (g_ProfOptions & OPT_VERBOSE)
  1567.                     {
  1568.                         WriteOutput("Class ");
  1569.                         WriteOutputUtf8(pszClassUtf8);
  1570.                         WriteOutput(" unloaded.\n");
  1571.                     }
  1572.  
  1573.                     CoTaskMemFree(pszClassUtf8);
  1574.                 }
  1575.             }
  1576.             else
  1577.             {
  1578.                 // Sorry.  The IE4 VM provided a PCSTR instead of a ClassID.
  1579.  
  1580.                 if (g_ProfOptions & OPT_VERBOSE)
  1581.                     WriteOutputFmt("Class %s unloaded.\n", (PCSTR)event_id);
  1582.             }
  1583.  
  1584.             break;
  1585.         }
  1586.  
  1587.         case JVM_EVENT_TYPE_GC_STARTED:         // The garbage collector started (no ID)
  1588.             m_gcprof.GCStarted();
  1589.             break;
  1590.  
  1591.         case JVM_EVENT_TYPE_GC_FINISHED:        // The garbage collector stopped (no ID)
  1592.             if (g_ProfOptions & OPT_VERBOSE)
  1593.                 WriteOutput("Garbage collection occurred.\n");
  1594.             m_gcprof.GCFinished();
  1595.             m_allocprof.GCOccurred();
  1596.             break;
  1597.  
  1598.         case JVM_EVENT_TYPE_SHUTDOWN:           // Program exiting (no ID).
  1599.             ShutdownWorker(TRUE);
  1600.             break;
  1601.  
  1602.         default:
  1603.             // What is this?
  1604.             break;
  1605.     }
  1606.  
  1607.     return S_OK;
  1608. }
  1609.  
  1610.  
  1611. STDMETHODIMP EventMonitor::MethodEntry (MethodID method_id, StackID stack_id)
  1612. {
  1613.     ThreadRecord *pThread = GetCurrentThreadRecord();
  1614.     if (pThread)
  1615.     {
  1616.         m_calltracer.OnEnterMethod(pThread, method_id, stack_id);
  1617.  
  1618.         m_callprof.EnterMethod(pThread, method_id);
  1619.     }
  1620.  
  1621.     return S_OK;
  1622. }
  1623.  
  1624.  
  1625. STDMETHODIMP EventMonitor::MethodExit(StackID stack_id)
  1626. {
  1627.     ThreadRecord *pThread = GetCurrentThreadRecord();
  1628.     if (pThread)
  1629.     {
  1630.         m_callprof.ExitMethod(pThread);
  1631.  
  1632.         m_calltracer.OnMethodExit(pThread, NULL, stack_id);
  1633.     }
  1634.  
  1635.     return S_OK;
  1636. }
  1637.  
  1638.  
  1639. STDMETHODIMP EventMonitor::ExecuteByteCode(MethodID method_id, BYTE_CODE *pbyte_code, DWORD byte_code_offset)
  1640. {
  1641.     return S_OK;
  1642. }
  1643.  
  1644.  
  1645. STDMETHODIMP EventMonitor::ExecuteSourceLine(MethodID method_id, DWORD line_number)
  1646. {
  1647.     return(S_OK);
  1648. }
  1649.  
  1650.  
  1651. STDMETHODIMP EventMonitor::NotifyEvent2(JVM_EVENT_TYPE2 event2, UniqueID first_event_id, UniqueID second_event_id)
  1652. {
  1653.     HRESULT hr = S_OK;
  1654.  
  1655.     switch (event2)
  1656.     {
  1657.     ThreadRecord *pThread;
  1658.  
  1659.     // A thread's name has been set.  ID1 is ThreadID.  ID2 is pointer to Unicode string thread name.
  1660.     case JVM_EVENT_TYPE2_THREAD_SET_NAME:
  1661.         if (g_ProfOptions & OPT_VERBOSE)
  1662.         {
  1663.             ThreadID vmid = (ThreadID)first_event_id;
  1664.             PCWSTR newname = (PCWSTR)second_event_id;
  1665.  
  1666.             if (m_monitor_info2)
  1667.             {
  1668.                 DWORD tid;
  1669.                 if (SUCCEEDED(m_monitor_info2->ThreadInformation(vmid, &tid)))
  1670.                 {
  1671.                     WriteOutputFmt("Thread %x's name changed to \"", tid);
  1672.                     WriteOutputW(newname);
  1673.                     WriteOutput("\".\n");
  1674.                 }
  1675.             }
  1676.         }
  1677.         break;
  1678.  
  1679.     // An exception is about to be thrown.  ID1 is the ClassID of the exception, ID2 is the ObjectID of the exception.
  1680.     case JVM_EVENT_TYPE2_EXCEPTION_THROWN:
  1681.         pThread = GetCurrentThreadRecord();
  1682.         if (pThread)
  1683.             m_callprof.ExceptionThrown(pThread);
  1684.  
  1685.         if (g_ProfOptions & OPT_VERBOSE)
  1686.         {
  1687.             PSTR pszExceptionUtf8;
  1688.             if (SUCCEEDED(m_monitor_info->ClassInformation((ClassID)first_event_id, &pszExceptionUtf8, NULL, NULL, NULL, NULL)))
  1689.             {
  1690.                 WriteOutput("Exception ");
  1691.                 WriteOutputUtf8(pszExceptionUtf8);
  1692.                 WriteOutput(" \n");
  1693.  
  1694.                 CoTaskMemFree(pszExceptionUtf8);
  1695.             }
  1696.         }
  1697.  
  1698.         break;
  1699.  
  1700.     // An exception occurred.  ID1 is MethodID, ID2 is StackID.  The exception handler will be executed in MethodID/StackID's stack frame.
  1701.     case JVM_EVENT_TYPE2_EXCEPTION_OCCURRED:
  1702.         pThread = GetCurrentThreadRecord();
  1703.         if (pThread)
  1704.             m_callprof.ResumeMethod(pThread, (MethodID)first_event_id);
  1705.         break;
  1706.  
  1707.     // Callback from GetStackTrace.  ID1 is MethodID, ID2 is StackID.
  1708.     case JVM_EVENT_TYPE2_STACK_TRACE:
  1709.         pThread = GetCurrentThreadRecord();
  1710.         pThread->allocdata.idCreatingMethod = (MethodID)first_event_id;
  1711.         hr = S_FALSE;
  1712.         break;
  1713.  
  1714.     // The vm has fully initialized.
  1715.     case JVM_EVENT_TYPE2_INITIALIZED:
  1716.         OnVMInitialization();
  1717.         break;
  1718.  
  1719.     // All event monitors have initialized.  ID1 is the number of event monitors
  1720.     // attached to the VM. ID2 is a bitmask of JAVA_EVENT_CATEGORY, indicating the
  1721.     // maximum possible set of events that may be enabled by all attached profilers.
  1722.     case JVM_EVENT_TYPE2_MONITORS_INITIALIZED:
  1723.  
  1724.         unsigned nmonitors;
  1725.         nmonitors = (unsigned)first_event_id;
  1726.  
  1727.         if (nmonitors > 1)
  1728.             WriteOutputFmt("%u other profilers are attached.\n", nmonitors-1);
  1729.  
  1730.         if (g_ProfOptions & OPT_VERBOSE)
  1731.         {
  1732.             DWORD possible_events = (DWORD)second_event_id;
  1733.  
  1734. #if 0                
  1735.             Spew(SPEW_REG, "%u monitors, possible events:", nmonitors);
  1736.             SpewCategories(SPEW_REG, possible_events);
  1737.             Spew(SPEW_REG, "\n");
  1738. #endif
  1739.  
  1740.         }
  1741.  
  1742.         if (m_monitor_info4 != NULL)
  1743.         {
  1744.             DWORD initopts;
  1745.             hr = m_monitor_info4->GetFinalInitializationOptions(&initopts);
  1746.             if (hr == S_OK)
  1747.             {
  1748. #if 0                
  1749.                 Spew(SPEW_REG, "final initialization options:");
  1750.                 SpewInitOptions(SPEW_REG, initopts);
  1751.                 Spew(SPEW_REG, "\n");
  1752. #endif
  1753.             }
  1754.         }
  1755.  
  1756.         break;
  1757.     }
  1758.  
  1759.     return(hr);
  1760. }
  1761.  
  1762.  
  1763. STDMETHODIMP EventMonitor::MethodExit2(MethodID method_id, StackID stack_id)
  1764. {
  1765.     ThreadRecord *pThread = GetCurrentThreadRecord();
  1766.     if (pThread)
  1767.     {
  1768.         m_callprof.ExitMethod(pThread);
  1769.  
  1770.         m_calltracer.OnMethodExit(pThread, method_id, stack_id);
  1771.  
  1772.         m_callprof.ResumeMethod(pThread, method_id);
  1773.     }
  1774.  
  1775.     return S_OK;
  1776. }
  1777.  
  1778.  
  1779. STDMETHODIMP EventMonitor::GetPossibleEventCategories (DWORD *ppossible_events)
  1780. {
  1781.     // We don't toggle any events.
  1782.     return m_monitor_info->GetEventMask(ppossible_events);
  1783. }
  1784.  
  1785.  
  1786. //------------------------------------------------------------------------
  1787.  
  1788.  
  1789. STDMETHODIMP EventMonitor::OnObjectAllocated (ObjectID idObject, ClassID idType)
  1790. {
  1791.     DWORD size = 0;
  1792.     if (m_monitor_info2 != NULL)
  1793.         if (m_monitor_info2->GetObjectSize(idObject, &size) != S_OK)
  1794.             size = 0;
  1795.  
  1796.     MethodID idCreatingMethod = NULL;
  1797.     if (g_ProfOptions & OPT_ALLOC_PER_METHOD)
  1798.     {
  1799.         ThreadRecord *pThread = GetCurrentThreadRecord();
  1800.         if (pThread != NULL)
  1801.         {
  1802.             if (g_ProfOptions & OPT_PROF_CALLS)
  1803.             {
  1804.                 // If there is no current entry, then the allocation occurred
  1805.                 // in native code.  (For example, the VM performs allocations
  1806.                 // during initialization, before any methods are called.)
  1807.                 CallProfiler::CallProfilerEntry *pent = pThread->calldata.pentCurMethod;
  1808.                 if (pent)
  1809.                     idCreatingMethod = pent->id;
  1810.             }
  1811.             else
  1812.             {
  1813.                 pThread->allocdata.idCreatingMethod = NULL;
  1814.                 m_monitor_info2->GetStackTrace(NULL);
  1815.                 idCreatingMethod = pThread->allocdata.idCreatingMethod;
  1816.             }
  1817.         }
  1818.     }
  1819.  
  1820.     m_allocprof.InstanceCreated(idType, size, idCreatingMethod);
  1821.  
  1822.     return S_OK;
  1823. }
  1824.  
  1825.