Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1682 lines
65 KiB

  1. #include "priv.h"
  2. #include "shlwapip.h"
  3. #include "mshtmdbg.h"
  4. #define STOPWATCH_MAX_DESC 256
  5. #define STOPWATCH_MAX_TITLE 192
  6. #define STOPWATCH_MAX_BUF 1024
  7. // Perftags defines and typedefs
  8. typedef PERFTAG (WINAPI *PFN_PERFREGISTER)(char *, char *, char *);
  9. typedef void (WINAPIV *PFN_PERFLOGFN)(PERFTAG, void *, const char *, ...);
  10. typedef char *(WINAPI *PFN_DECODEMESSAGE)(INT);
  11. // IceCAP function typedefs
  12. typedef void (WINAPI *PFN_ICAP)(void);
  13. // MemWatch function typedefs
  14. typedef HRESULT (WINAPI *PFN_MWCONFIG)(DWORD, DWORD, DWORD);
  15. typedef HRESULT (WINAPI *PFN_MWBEGIN)(BOOL, BOOL);
  16. typedef HRESULT (WINAPI *PFN_MWSNAPSHOT)();
  17. typedef HRESULT (WINAPI *PFN_MWEND)(char *);
  18. typedef HRESULT (WINAPI *PFN_MWMARK)(char *);
  19. typedef HRESULT (WINAPI *PFN_MWEXIT)();
  20. #ifndef NO_ETW_TRACING
  21. #include <wmistr.h>
  22. #include <evntrace.h>
  23. typedef TRACEHANDLE (WINAPI *PFN_GTLOGHANDLE)(PVOID);
  24. typedef ULONG (WINAPI *PFN_REGTRACE)(WMIDPREQUEST,
  25. PVOID,
  26. LPCGUID,
  27. ULONG,
  28. PTRACE_GUID_REGISTRATION,
  29. LPCTSTR,
  30. LPCTSTR,
  31. PTRACEHANDLE
  32. );
  33. typedef ULONG (WINAPI *PFN_UNREGTRACE)(TRACEHANDLE);
  34. typedef ULONG (WINAPI *PFN_TRACE)(TRACEHANDLE, PEVENT_TRACE_HEADER);
  35. #endif
  36. // Stopwatch memory buffer
  37. typedef struct _STOPWATCH
  38. {
  39. DWORD dwId; // Node identifier
  40. DWORD dwTID; // Thread ID;
  41. DWORD dwType; // Node type - start, lap, stop, emtpy
  42. DWORD dwCount; // Tick count
  43. DWORD dwFlags; // Node flags - memlog, debugout
  44. TCHAR szDesc[STOPWATCH_MAX_DESC];
  45. } STOPWATCH, *PSTOPWATCH;
  46. // Global stopwatch info data
  47. typedef struct _STOPWATCHINFO
  48. {
  49. DWORD dwStopWatchMode;
  50. DWORD dwStopWatchProfile;
  51. DWORD dwStopWatchListIndex;
  52. DWORD dwStopWatchListMax;
  53. DWORD dwStopWatchPaintInterval;
  54. // SPMODE_MSGTRACE data
  55. DWORD dwStopWatchMaxDispatchTime;
  56. DWORD dwStopWatchMaxMsgTime;
  57. DWORD dwStopWatchMsgInterval;
  58. DWORD dwcStopWatchOverflow;
  59. DWORD dwStopWatchLastLocation;
  60. DWORD dwStopWatchTraceMsg;
  61. DWORD dwStopWatchTraceMsgCnt;
  62. DWORD *pdwStopWatchMsgTime;
  63. // SPMODE_MEMWATCH config data and function pointers
  64. DWORD dwMemWatchPages;
  65. DWORD dwMemWatchTime;
  66. DWORD dwMemWatchFlags;
  67. BOOL fMemWatchConfig;
  68. HMODULE hModMemWatch;
  69. PFN_MWCONFIG pfnMemWatchConfig;
  70. PFN_MWBEGIN pfnMemWatchBegin;
  71. PFN_MWSNAPSHOT pfnMemWatchSnapShot;
  72. PFN_MWEND pfnMemWatchEnd;
  73. PFN_MWMARK pfnMemWatchMark;
  74. PFN_MWEXIT pfnMemWatchExit;
  75. // Perftag data and function pointers
  76. PERFTAG tagStopWatchStart;
  77. PERFTAG tagStopWatchStop;
  78. PERFTAG tagStopWatchLap;
  79. PFN_PERFREGISTER pfnPerfRegister;
  80. PFN_PERFLOGFN pfnPerfLogFn;
  81. PFN_DECODEMESSAGE pfnDecodeMessage;
  82. #ifndef NO_ETW_TRACING
  83. DWORD dwEventTraceMode;
  84. PFN_GTLOGHANDLE pfnGetLogHandle;
  85. PFN_REGTRACE pfnRegisterTraceGuids;
  86. PFN_UNREGTRACE pfnUnRegisterTraceGuids;
  87. PFN_TRACE pfnTraceEvent;
  88. #endif
  89. LPTSTR pszClassNames;
  90. PSTOPWATCH pStopWatchList;
  91. // IceCAP data and function pointers
  92. HMODULE hModICAP;
  93. PFN_ICAP pfnStartCAPAll;
  94. PFN_ICAP pfnStopCAPAll;
  95. HANDLE hMapHtmPerfCtl;
  96. HTMPERFCTL *pHtmPerfCtl;
  97. } STOPWATCHINFO, *PSTOPWATCHINFO;
  98. #ifndef NO_ETW_TRACING
  99. #define c_szBrowserResourceName TEXT("Browse")
  100. // Used to turn on/off event tracing. Setting the registry key enable event
  101. // tracing use, but doesn't turn it on.
  102. // {5576F62E-4142-45a8-9516-262A510C13F0}
  103. const GUID c_BrowserControlGuid = {
  104. 0x5576f62e,
  105. 0x4142,
  106. 0x45a8,
  107. 0x95, 0x16, 0x26, 0x2a, 0x51, 0xc, 0x13, 0xf0};
  108. // Maps to the structure sent to ETW. ETW definition in
  109. // \nt\sdktools\trace\tracedmp\mofdata.guid
  110. // {2B992163-736F-4a68-9153-95BC5F34D884}
  111. const GUID c_BrowserTraceGuid = {
  112. 0x2b992163,
  113. 0x736f,
  114. 0x4a68,
  115. 0x91, 0x53, 0x95, 0xbc, 0x5f, 0x34, 0xd8, 0x84};
  116. TRACE_GUID_REGISTRATION g_BrowserTraceGuidReg[] =
  117. {
  118. { (LPGUID)&c_BrowserTraceGuid,
  119. NULL
  120. }
  121. };
  122. //
  123. // The mof fields point to the following data.
  124. // MOF_FIELD MofData[0]; // Holds ptr to Url Name
  125. //
  126. typedef struct _ETW_BROWSER_EVENT {
  127. EVENT_TRACE_HEADER Header;
  128. MOF_FIELD MofData[1];
  129. } ETW_BROWSER_EVENT, *PETW_BROWSER_EVENT;
  130. static TRACEHANDLE s_hEtwBrowserRegHandle;
  131. static TRACEHANDLE s_hEtwBrowserLogHandle;
  132. // For SHInterlockedCompareExchange
  133. static BOOL s_fTRUE = TRUE;
  134. static PVOID s_pvEtwBrowserTraceOnFlag = NULL;
  135. static PVOID s_pvEtwBrowserRegistered = NULL;
  136. static PVOID s_pvEtwBrowserRegistering = NULL;
  137. #endif
  138. PSTOPWATCHINFO g_pswi = NULL;
  139. const TCHAR c_szDefClassNames[] = {STOPWATCH_DEFAULT_CLASSNAMES};
  140. void StopWatch_SignalEvent();
  141. //===========================================================================================
  142. // INTERNAL FUNCTIONS
  143. //===========================================================================================
  144. //===========================================================================================
  145. //===========================================================================================
  146. void PerfCtlCallback(DWORD dwArg1, void * pvArg2)
  147. {
  148. const TCHAR c_szFmtBrowserStop[] = TEXT("Browser Frame Stop (%s)");
  149. TCHAR szTitle[STOPWATCH_MAX_TITLE];
  150. TCHAR szText[STOPWATCH_MAX_TITLE + ARRAYSIZE(c_szFmtBrowserStop) + 1];
  151. LPTSTR ptr = szTitle;
  152. #ifndef UNICODE
  153. INT rc;
  154. #endif
  155. if(g_pswi->dwStopWatchMode & SPMODE_BROWSER) // Temp hack to deal with ansi,unicode. This code will go away when we impl hook in mshtml.
  156. {
  157. // GetWindowText(hwnd, szTitle, ARRAYSIZE(szTitle)-1);
  158. #ifndef UNICODE
  159. rc = WideCharToMultiByte(CP_ACP, 0, pvArg2, -1, szTitle, STOPWATCH_MAX_TITLE - 1, NULL, NULL);
  160. if(!rc)
  161. StrCpyN(szTitle, "ERROR converting wide to multi", ARRAYSIZE(szTitle) - 1);
  162. #else
  163. ptr = (LPTSTR) pvArg2;
  164. #endif
  165. wnsprintf(szText, ARRAYSIZE(szText), c_szFmtBrowserStop, ptr);
  166. StopWatch_Stop(SWID_BROWSER_FRAME, szText, SPMODE_BROWSER | SPMODE_DEBUGOUT);
  167. if((g_pswi->dwStopWatchMode & (SPMODE_EVENT | SPMODE_BROWSER)) == (SPMODE_EVENT | SPMODE_BROWSER))
  168. {
  169. StopWatch_SignalEvent();
  170. }
  171. }
  172. }
  173. #ifndef NO_ETW_TRACING
  174. /*++
  175. Routine Name:
  176. ulEtwBrowserControlCallback()
  177. Routine Description:
  178. This is the function we provide to the ETW subsystem as a callback, it is used to
  179. start and stop the trace events.
  180. Arguments:
  181. IN WMIDPREQUESTCODE RequestCode : The function to provide (enable/disable)
  182. IN PVOID Context : Not used by us.
  183. IN OUT ULONG *InOutBufferSize : The Buffersize
  184. IN OUT PVOID Buffer : The buffer to use for the events
  185. Returns ERROR_SUCCESS on success, or an error code.
  186. --*/
  187. ULONG
  188. ulEtwBrowserControlCallback(
  189. IN WMIDPREQUESTCODE RequestCode,
  190. IN PVOID pvContext,
  191. IN OUT ULONG *InOutBufferSize,
  192. IN OUT PVOID pvBuffer
  193. )
  194. {
  195. ULONG Status;
  196. if (!s_pvEtwBrowserRegistered) {
  197. // Registration hasn't happened yet.
  198. return ERROR_GEN_FAILURE;
  199. }
  200. Status = ERROR_SUCCESS;
  201. switch (RequestCode)
  202. {
  203. case WMI_ENABLE_EVENTS:
  204. {
  205. #if STOPWATCH_DEBUG
  206. OutputDebugString("shperf.c:ulEtwBrowserControlCallback enable\n");
  207. #endif
  208. ASSERT(g_pswi->pfnGetLogHandle);
  209. s_hEtwBrowserLogHandle = g_pswi->pfnGetLogHandle( pvBuffer );
  210. #if STOPWATCH_DEBUG
  211. if (s_hEtwBrowserLogHandle == INVALID_HANDLE_VALUE) {
  212. TCHAR szDbg[256];
  213. wnsprintf(szDbg, ARRAYSIZE(szDbg) - 1,
  214. "ulEtwBrowserControlCallback GLE=%u\n", GetLastError());
  215. OutputDebugString(szDbg);
  216. }
  217. #endif
  218. SHInterlockedCompareExchange(&s_pvEtwBrowserTraceOnFlag, &s_fTRUE, NULL);
  219. break;
  220. }
  221. case WMI_DISABLE_EVENTS:
  222. {
  223. #if STOPWATCH_DEBUG
  224. OutputDebugString("shperf.c:ulEtwBrowserControlCallback disable\n");
  225. #endif
  226. SHInterlockedCompareExchange(&s_pvEtwBrowserTraceOnFlag, NULL, &s_fTRUE);
  227. s_hEtwBrowserLogHandle = 0;
  228. break;
  229. }
  230. default:
  231. {
  232. Status = ERROR_INVALID_PARAMETER;
  233. break;
  234. }
  235. }
  236. *InOutBufferSize = 0;
  237. return(Status);
  238. }
  239. /*++
  240. Routine Name:
  241. RegisterTracing()
  242. Routine Description:
  243. Registers us to the ETW tools
  244. Arguments:
  245. Returns ERROR_SUCCESS on success of all registrations.
  246. --*/
  247. ULONG RegisterTracing()
  248. {
  249. ULONG Status = ERROR_SUCCESS;
  250. TCHAR szImagePath[MAX_PATH];
  251. Status = GetModuleFileName(NULL, szImagePath, sizeof(szImagePath)/sizeof(TCHAR));
  252. if (Status == 0) {
  253. Status = ERROR_FILE_NOT_FOUND;
  254. }
  255. else {
  256. if (g_pswi->dwEventTraceMode & SPTRACE_BROWSER) {
  257. PVOID fRegOn;
  258. // If browser tracing is not registered, register it, preventing
  259. // anybody else from trying to do this at the same time.
  260. fRegOn = SHInterlockedCompareExchange(&s_pvEtwBrowserRegistering, &s_fTRUE, NULL);
  261. if (!fRegOn) {
  262. if (!s_pvEtwBrowserRegistered) {
  263. ASSERT(g_pswi->pfnRegisterTraceGuids);
  264. Status = g_pswi->pfnRegisterTraceGuids(
  265. ulEtwBrowserControlCallback,
  266. NULL,
  267. (LPGUID)&c_BrowserControlGuid,
  268. 1,
  269. g_BrowserTraceGuidReg,
  270. szImagePath,
  271. c_szBrowserResourceName,
  272. &s_hEtwBrowserRegHandle);
  273. if (Status == ERROR_SUCCESS) {
  274. SHInterlockedCompareExchange(&s_pvEtwBrowserRegistered, &s_fTRUE, NULL);
  275. }
  276. #if STOPWATCH_DEBUG
  277. else {
  278. OutputDebugString("shperf.c:Registration of event tracing guids failed.\n");
  279. }
  280. #endif
  281. }
  282. SHInterlockedCompareExchange(&s_pvEtwBrowserRegistering, NULL, &s_fTRUE);
  283. }
  284. }
  285. }
  286. return Status;
  287. }
  288. /*++
  289. Routine Name:
  290. UnRegisterTracing()
  291. Routine Description:
  292. Deregisters us from the ETW tools
  293. Arguments:
  294. Returns ERROR_SUCCESS on success. a Winerror otherwise.
  295. --*/
  296. ULONG UnRegisterTracing()
  297. {
  298. ULONG Status = ERROR_SUCCESS;
  299. // If browser tracing is registered, unregister it.
  300. if (s_pvEtwBrowserRegistered) {
  301. SHInterlockedCompareExchange(&s_pvEtwBrowserTraceOnFlag, NULL, &s_fTRUE);
  302. if(g_pswi && g_pswi->pfnUnRegisterTraceGuids) {
  303. Status = g_pswi->pfnUnRegisterTraceGuids(s_hEtwBrowserRegHandle);
  304. }
  305. if (Status == ERROR_SUCCESS) {
  306. SHInterlockedCompareExchange(&s_pvEtwBrowserRegistered, NULL, &s_fTRUE);
  307. }
  308. #if STOPWATCH_DEBUG
  309. else {
  310. OutputDebugString("shperf.c:UnRegistration of event tracing guids failed.\n");
  311. }
  312. #endif
  313. }
  314. return Status;
  315. }
  316. /*++
  317. Routine Name:
  318. EventTraceHandler()
  319. Routine Description:
  320. If tracing is turned on, this sends the event to the WMI subsystem.
  321. Arguments:
  322. UCHAR EventType : Kind of trace event
  323. PVOID Data : Data associated with event
  324. --*/
  325. void WINAPI EventTraceHandler(UCHAR uchEventType, PVOID pvData)
  326. {
  327. if ((g_pswi->dwEventTraceMode & SPTRACE_BROWSER))
  328. {
  329. if (s_pvEtwBrowserTraceOnFlag)
  330. {
  331. ETW_BROWSER_EVENT EtwEvent;
  332. ULONG Status;
  333. LPWSTR wszUrl = pvData;
  334. //
  335. // Record data.
  336. //
  337. ZeroMemory(&EtwEvent, sizeof(EtwEvent));
  338. EtwEvent.Header.Size = sizeof(ETW_BROWSER_EVENT);
  339. EtwEvent.Header.Flags = (WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR);
  340. EtwEvent.Header.Class.Type = uchEventType;
  341. EtwEvent.Header.Guid = c_BrowserTraceGuid;
  342. EtwEvent.MofData[0].DataPtr = (ULONG64)wszUrl;
  343. EtwEvent.MofData[0].Length = (wszUrl
  344. ? (wcslen(wszUrl)+1)*sizeof(WCHAR)
  345. : 0);
  346. ASSERT(g_pswi->pfnTraceEvent);
  347. Status = g_pswi->pfnTraceEvent(
  348. s_hEtwBrowserLogHandle,
  349. (PEVENT_TRACE_HEADER) &EtwEvent);
  350. #if STOPWATCH_DEBUG
  351. if (Status != ERROR_SUCCESS) {
  352. TCHAR szDbg[256];
  353. wnsprintf(szDbg, ARRAYSIZE(szDbg) - 1,
  354. "shperf.c:Call to trace event failed %I64x GLE=%u\n",
  355. s_hEtwBrowserLogHandle, Status);
  356. OutputDebugString(szDbg);
  357. }
  358. #endif
  359. }
  360. // Signal event when full web page is downloaded.
  361. if ((uchEventType == EVENT_TRACE_TYPE_BROWSE_LOADEDPARSED) &&
  362. (g_pswi->dwStopWatchMode & SPMODE_EVENT)) {
  363. StopWatch_SignalEvent();
  364. }
  365. }
  366. }
  367. // Called through a pointer in the shared memory map section.
  368. void PerfCtlEvntCallback(DWORD dwArg1, void * pvArg2)
  369. {
  370. EventTraceHandler((UCHAR)dwArg1, pvArg2);
  371. }
  372. #endif
  373. //===========================================================================================
  374. //===========================================================================================
  375. HRESULT SetPerfCtl(DWORD dwFlags)
  376. {
  377. if (dwFlags == HTMPF_CALLBACK_ONLOAD ||
  378. dwFlags == HTMPF_CALLBACK_ONEVENT)
  379. {
  380. char achName[sizeof(HTMPERFCTL_NAME) + 8 + 1];
  381. wsprintfA(achName, "%s%08lX", HTMPERFCTL_NAME, GetCurrentProcessId());
  382. if (g_pswi->hMapHtmPerfCtl == NULL)
  383. g_pswi->hMapHtmPerfCtl = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, achName);
  384. if (g_pswi->hMapHtmPerfCtl == NULL)
  385. return(E_FAIL);
  386. if (g_pswi->pHtmPerfCtl == NULL)
  387. g_pswi->pHtmPerfCtl = (HTMPERFCTL *)MapViewOfFile(g_pswi->hMapHtmPerfCtl, FILE_MAP_WRITE, 0, 0, 0);
  388. if (g_pswi->pHtmPerfCtl == NULL)
  389. return(E_FAIL);
  390. g_pswi->pHtmPerfCtl->dwSize = sizeof(HTMPERFCTL);
  391. g_pswi->pHtmPerfCtl->dwFlags = dwFlags;
  392. #ifndef NO_ETW_TRACING
  393. if (dwFlags == HTMPF_CALLBACK_ONEVENT) {
  394. RegisterTracing();
  395. // Used by clients, like mshtml, to log events.
  396. g_pswi->pHtmPerfCtl->pfnCall = PerfCtlEvntCallback;
  397. }
  398. else
  399. #endif
  400. g_pswi->pHtmPerfCtl->pfnCall = PerfCtlCallback;
  401. g_pswi->pHtmPerfCtl->pvHost = NULL;
  402. }
  403. return S_OK;
  404. }
  405. //===========================================================================================
  406. //===========================================================================================
  407. void StopWatch_SignalEvent()
  408. {
  409. static HANDLE hEvent = NULL;
  410. if(hEvent == NULL)
  411. {
  412. TCHAR szEventName[256];
  413. wnsprintf(szEventName, ARRAYSIZE(szEventName), TEXT("%s%x"), TEXT("STOPWATCH_STOP_EVENT"), GetCurrentProcessId());
  414. hEvent = CreateEvent((LPSECURITY_ATTRIBUTES)NULL, FALSE, FALSE, szEventName);
  415. }
  416. if(hEvent != NULL)
  417. SetEvent(hEvent);
  418. }
  419. //===========================================================================================
  420. //===========================================================================================
  421. HRESULT DoMemWatchConfig(VOID)
  422. {
  423. HRESULT hr = ERROR_SUCCESS;
  424. if(g_pswi->hModMemWatch == NULL)
  425. {
  426. if((g_pswi->hModMemWatch = LoadLibrary("mwshelp.dll")) != NULL)
  427. {
  428. g_pswi->pfnMemWatchConfig = (PFN_MWCONFIG) GetProcAddress(g_pswi->hModMemWatch, "MemWatchConfigure");
  429. g_pswi->pfnMemWatchBegin = (PFN_MWBEGIN) GetProcAddress(g_pswi->hModMemWatch, "MemWatchBegin");
  430. g_pswi->pfnMemWatchSnapShot = (PFN_MWSNAPSHOT) GetProcAddress(g_pswi->hModMemWatch, "MemWatchSnapShot");
  431. g_pswi->pfnMemWatchEnd = (PFN_MWEND) GetProcAddress(g_pswi->hModMemWatch, "MemWatchEnd");
  432. g_pswi->pfnMemWatchMark = (PFN_MWMARK) GetProcAddress(g_pswi->hModMemWatch, "MemWatchMark");
  433. g_pswi->pfnMemWatchExit = (PFN_MWEXIT) GetProcAddress(g_pswi->hModMemWatch, "MemWatchExit");
  434. if(g_pswi->pfnMemWatchConfig != NULL)
  435. {
  436. hr = g_pswi->pfnMemWatchConfig(g_pswi->dwMemWatchPages, g_pswi->dwMemWatchTime, g_pswi->dwMemWatchFlags);
  437. if(FAILED(hr))
  438. g_pswi->dwStopWatchMode &= ~SPMODE_MEMWATCH;
  439. else
  440. g_pswi->fMemWatchConfig = TRUE;
  441. }
  442. }
  443. else
  444. {
  445. g_pswi->hModMemWatch = (HMODULE)1;
  446. }
  447. }
  448. return(hr);
  449. }
  450. //===========================================================================================
  451. // Function: VOID InitStopWatchMode(VOID)
  452. //
  453. // If HKLM\software\microsoft\windows\currentversion\explorer\performance\mode key value
  454. // is set to one of the values described below, the stopwatch mode will be enabled by
  455. // setting the global variable g_pswi->dwStopWatchMode.
  456. //
  457. // SPMODE_SHELL - Allows the flushing of stopwatch timings to a log file
  458. // SPMODE_DEBUGOUT - Display timing via OutputDebugString. Only timings marked with SPMODE_DEBUGOUT
  459. // through the StopWatch_* calls will be displayed.
  460. // SPMODE_TEST - Used to display test output. This allow another level of SPMODE_DEBUGOUT
  461. // like output.
  462. //
  463. // If HKLM\software\microsoft\windows\currentversion\explorer\performance\nodes key value
  464. // is set, the size of the timing array will be set to this value. The default is 100 nodes.
  465. //===========================================================================================
  466. #define REGKEY_PERFMODE REGSTR_PATH_EXPLORER TEXT("\\Performance")
  467. VOID InitStopWatchMode(VOID)
  468. {
  469. HKEY hkeyPerfMode;
  470. DWORD dwVal = 0;
  471. DWORD cbBuffer;
  472. DWORD dwType;
  473. TCHAR szClassNames[256];
  474. #if STOPWATCH_DEBUG
  475. TCHAR szDbg[256];
  476. #endif
  477. if(NO_ERROR == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_PERFMODE, 0L, MAXIMUM_ALLOWED, &hkeyPerfMode))
  478. {
  479. cbBuffer = SIZEOF(dwVal);
  480. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("Mode"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  481. {
  482. if((dwVal & SPMODES) == 0) // Low word is mode, high word is paint timer interval
  483. dwVal |= SPMODE_SHELL;
  484. if((g_pswi = (PSTOPWATCHINFO)LocalAlloc(LPTR, SIZEOF(STOPWATCHINFO))) == NULL)
  485. dwVal = 0;
  486. }
  487. if(dwVal != 0)
  488. {
  489. g_pswi->dwStopWatchMode = dwVal;
  490. g_pswi->dwStopWatchListMax = STOPWATCH_MAX_NODES;
  491. g_pswi->dwStopWatchPaintInterval = STOPWATCH_DEFAULT_PAINT_INTERVAL;
  492. g_pswi->dwStopWatchMaxDispatchTime = STOPWATCH_DEFAULT_MAX_DISPATCH_TIME;
  493. g_pswi->dwStopWatchMaxMsgTime = STOPWATCH_DEFAULT_MAX_MSG_TIME;
  494. g_pswi->dwStopWatchMsgInterval = STOPWATCH_DEFAULT_MAX_MSG_INTERVAL;
  495. g_pswi->pszClassNames = (LPTSTR)c_szDefClassNames;
  496. g_pswi->dwMemWatchPages = MEMWATCH_DEFAULT_PAGES;
  497. g_pswi->dwMemWatchTime = MEMWATCH_DEFAULT_TIME;
  498. g_pswi->dwMemWatchFlags = MEMWATCH_DEFAULT_FLAGS;
  499. #ifndef NO_ETW_TRACING
  500. g_pswi->dwEventTraceMode = 0;
  501. #endif
  502. cbBuffer = SIZEOF(dwVal);
  503. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("Profile"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  504. g_pswi->dwStopWatchProfile = dwVal;
  505. cbBuffer = SIZEOF(dwVal);
  506. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("Nodes"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  507. g_pswi->dwStopWatchListMax = dwVal;
  508. cbBuffer = SIZEOF(szClassNames);
  509. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("ClassNames"), NULL, &dwType, (LPBYTE)&szClassNames, &cbBuffer))
  510. {
  511. if((g_pswi->pszClassNames = (LPTSTR)LocalAlloc(LPTR, SIZEOF(LPTSTR) * cbBuffer)) != NULL)
  512. CopyMemory(g_pswi->pszClassNames, szClassNames, SIZEOF(LPTSTR) * cbBuffer);
  513. }
  514. cbBuffer = SIZEOF(dwVal);
  515. // begin - Remove this after StopWatch users convert to using PaintInterval key
  516. g_pswi->dwStopWatchPaintInterval = HIWORD(g_pswi->dwStopWatchMode) ?HIWORD(g_pswi->dwStopWatchMode) :STOPWATCH_DEFAULT_PAINT_INTERVAL; // Use high word of mode reg key value for interval
  517. // end - Remove this after StopWatch users convert to using PaintInterval key
  518. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("PaintInterval"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  519. g_pswi->dwStopWatchPaintInterval = dwVal;
  520. // Get MemWatch data
  521. cbBuffer = SIZEOF(dwVal);
  522. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("MWPages"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  523. g_pswi->dwMemWatchPages = dwVal;
  524. cbBuffer = SIZEOF(dwVal);
  525. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("MWTime"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  526. g_pswi->dwMemWatchTime = dwVal;
  527. cbBuffer = SIZEOF(dwVal);
  528. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("MWFlags"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  529. g_pswi->dwMemWatchFlags = dwVal;
  530. #ifndef NO_ETW_TRACING
  531. if (g_pswi->dwStopWatchMode & SPMODE_EVENTTRACE) {
  532. // Load the ETW operations individually to make sure the system
  533. // this proc is running on supports them.
  534. HMODULE hMod;
  535. if ((hMod = LoadLibrary("advapi32.dll")) != NULL) {
  536. g_pswi->pfnGetLogHandle = (PFN_GTLOGHANDLE) GetProcAddress(hMod, "GetTraceLoggerHandle");
  537. g_pswi->pfnUnRegisterTraceGuids = (PFN_UNREGTRACE) GetProcAddress(hMod, "UnregisterTraceGuids");
  538. g_pswi->pfnTraceEvent = (PFN_TRACE) GetProcAddress(hMod, "TraceEvent");
  539. g_pswi->pfnRegisterTraceGuids =
  540. #if defined(UNICODE) || defined(_UNICODE)
  541. (PFN_REGTRACE) GetProcAddress(hMod, "RegisterTraceGuidsW");
  542. #else
  543. (PFN_REGTRACE) GetProcAddress(hMod, "RegisterTraceGuidsA");
  544. #endif
  545. }
  546. else {
  547. g_pswi->pfnGetLogHandle = NULL;
  548. g_pswi->pfnUnRegisterTraceGuids = NULL;
  549. g_pswi->pfnTraceEvent = NULL;
  550. g_pswi->pfnRegisterTraceGuids = NULL;
  551. }
  552. if (!hMod ||
  553. !g_pswi->pfnGetLogHandle ||
  554. !g_pswi->pfnUnRegisterTraceGuids ||
  555. !g_pswi->pfnTraceEvent ||
  556. !g_pswi->pfnRegisterTraceGuids) {
  557. // Event trace calls will never be made now.
  558. #if STOPWATCH_DEBUG
  559. wnsprintf(szDbg, ARRAYSIZE(szDbg) - 1, "~SPMODE_EVENTTRACE load procs from advapi32.dll failed.\n");
  560. OutputDebugString(szDbg);
  561. #endif
  562. g_pswi->dwStopWatchMode &= ~SPMODE_EVENTTRACE;
  563. }
  564. else {
  565. // Set type of event tracing before setting up the call back.
  566. cbBuffer = SIZEOF(dwVal);
  567. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("EventTrace"), NULL,
  568. &dwType, (LPBYTE)&dwVal, &cbBuffer)) {
  569. g_pswi->dwEventTraceMode = dwVal;
  570. // You can only have one callback, so don't allow browser
  571. // stopwatch and browser event tracing.
  572. g_pswi->dwStopWatchMode &= ~SPMODE_BROWSER;
  573. }
  574. }
  575. }
  576. #endif
  577. if(g_pswi->dwStopWatchMode & SPMODES)
  578. {
  579. #ifndef NO_ETW_TRACING
  580. SetPerfCtl(g_pswi->dwStopWatchMode & SPMODE_EVENTTRACE
  581. // See mshtmdbg.h
  582. ? HTMPF_CALLBACK_ONEVENT
  583. : HTMPF_CALLBACK_ONLOAD);
  584. #else
  585. SetPerfCtl(HTMPF_CALLBACK_ONLOAD);
  586. #endif
  587. }
  588. if(g_pswi->dwStopWatchMode & SPMODE_MSGTRACE)
  589. {
  590. cbBuffer = SIZEOF(dwVal);
  591. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("MaxDispatchTime"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  592. g_pswi->dwStopWatchMaxDispatchTime = dwVal;
  593. cbBuffer = SIZEOF(dwVal);
  594. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("MaxMsgTime"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  595. g_pswi->dwStopWatchMaxMsgTime = dwVal;
  596. cbBuffer = SIZEOF(dwVal);
  597. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("MsgInterval"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  598. g_pswi->dwStopWatchMsgInterval = dwVal;
  599. cbBuffer = SIZEOF(dwVal);
  600. if(NO_ERROR == RegQueryValueEx(hkeyPerfMode, TEXT("TraceMsg"), NULL, &dwType, (LPBYTE)&dwVal, &cbBuffer))
  601. g_pswi->dwStopWatchTraceMsg = dwVal;
  602. if((g_pswi->pdwStopWatchMsgTime = (DWORD *)LocalAlloc(LPTR, sizeof(DWORD) * (g_pswi->dwStopWatchMaxMsgTime / g_pswi->dwStopWatchMsgInterval))) == NULL)
  603. g_pswi->dwStopWatchMode &= ~SPMODE_MSGTRACE;
  604. }
  605. if((g_pswi->pStopWatchList = (PSTOPWATCH)LocalAlloc(LPTR, sizeof(STOPWATCH)* g_pswi->dwStopWatchListMax)) == NULL) {
  606. g_pswi->dwStopWatchMode = 0;
  607. #ifndef NO_ETW_TRACING
  608. // Just in case this didn't fail too.
  609. UnRegisterTracing();
  610. #endif
  611. }
  612. if(g_pswi->dwStopWatchMode & SPMODE_PERFTAGS)
  613. {
  614. HMODULE hMod;
  615. if((hMod = LoadLibrary("mshtmdbg.dll")) != NULL)
  616. {
  617. g_pswi->pfnPerfRegister = (PFN_PERFREGISTER) GetProcAddress(hMod, "DbgExPerfRegister");
  618. g_pswi->pfnPerfLogFn = (PFN_PERFLOGFN) GetProcAddress(hMod, "DbgExPerfLogFn");
  619. g_pswi->pfnDecodeMessage = (PFN_DECODEMESSAGE) GetProcAddress(hMod, "DbgExDecodeMessage");
  620. }
  621. else
  622. {
  623. #if STOPWATCH_DEBUG
  624. wnsprintf(szDbg, ARRAYSIZE(szDbg) - 1, "~SPMODE_PERFTAGS loadlib mshtmdbg.dll failed GLE=0x%x\n", GetLastError());
  625. OutputDebugString(szDbg);
  626. #endif
  627. g_pswi->dwStopWatchMode &= ~SPMODE_PERFTAGS;
  628. }
  629. if(g_pswi->pfnPerfRegister != NULL)
  630. {
  631. g_pswi->tagStopWatchStart = g_pswi->pfnPerfRegister("tagStopWatchStart", "StopWatchStart", "SHLWAPI StopWatch start time");
  632. g_pswi->tagStopWatchStop = g_pswi->pfnPerfRegister("tagStopWatchStop", "StopWatchStop", "SHLWAPI StopWatch stop time");
  633. g_pswi->tagStopWatchLap = g_pswi->pfnPerfRegister("tagStopWatchLap", "StopWatchLap", "SHLWAPI StopWatch lap time");
  634. }
  635. }
  636. #ifdef STOPWATCH_DEBUG
  637. // Display option values
  638. {
  639. LPCTSTR ptr;
  640. wnsprintf(szDbg, ARRAYSIZE(szDbg) - 1, TEXT("StopWatch Mode=0x%x Profile=0x%x Nodes=%d PaintInterval=%d MemBuf=%d bytes\n"),
  641. g_pswi->dwStopWatchMode, g_pswi->dwStopWatchProfile, g_pswi->dwStopWatchListMax, g_pswi->dwStopWatchPaintInterval, g_pswi->dwStopWatchListMax * sizeof(STOPWATCH));
  642. OutputDebugString(szDbg);
  643. OutputDebugString(TEXT("Stopwatch ClassNames="));
  644. ptr = g_pswi->pszClassNames;
  645. while(*ptr)
  646. {
  647. wnsprintf(szDbg, ARRAYSIZE(szDbg) - 1, TEXT("'%s' "), ptr);
  648. OutputDebugString(szDbg);
  649. ptr = ptr + (lstrlen(ptr) + 1);
  650. }
  651. OutputDebugString(TEXT("\n"));
  652. if(g_pswi->dwStopWatchMode & SPMODE_MSGTRACE)
  653. {
  654. wnsprintf(szDbg, ARRAYSIZE(szDbg)-1, TEXT("StopWatch MaxDispatchTime=%dms MaxMsgTime=%dms MsgInterval=%dms TraceMsg=0x%x MemBuf=%d bytes\n"),
  655. g_pswi->dwStopWatchMaxDispatchTime, g_pswi->dwStopWatchMaxMsgTime, g_pswi->dwStopWatchMsgInterval, g_pswi->dwStopWatchTraceMsg, sizeof(DWORD) * (g_pswi->dwStopWatchMaxMsgTime / g_pswi->dwStopWatchMsgInterval));
  656. OutputDebugString(szDbg);
  657. }
  658. if(g_pswi->dwStopWatchMode & SPMODE_MEMWATCH)
  659. {
  660. wnsprintf(szDbg, ARRAYSIZE(szDbg)-1, TEXT("StopWatch MemWatch Pages=%d Time=%dms Flags=%d\n"),
  661. g_pswi->dwMemWatchPages, g_pswi->dwMemWatchTime, g_pswi->dwMemWatchFlags);
  662. OutputDebugString(szDbg);
  663. }
  664. }
  665. #endif
  666. } // if(dwVal != 0)
  667. RegCloseKey(hkeyPerfMode);
  668. }
  669. }
  670. //===========================================================================================
  671. // EXPORTED FUNCTIONS
  672. //===========================================================================================
  673. //===========================================================================================
  674. // Function: DWORD WINAPI StopWatchMode(VOID)
  675. //
  676. // Returns: The value of the global mode variable. Modules should use this call, set their
  677. // own global, and use this global to minimize and overhead when stopwatch mode
  678. // is not enabled.
  679. //===========================================================================================
  680. DWORD WINAPI StopWatchMode(VOID)
  681. {
  682. if(g_pswi != NULL)
  683. return(g_pswi->dwStopWatchMode);
  684. else
  685. return(0);
  686. }
  687. //===========================================================================================
  688. //===========================================================================================
  689. const TCHAR c_szBrowserStop[] = TEXT("Browser Frame Stop (%s)");
  690. DWORD MakeStopWatchDesc(DWORD dwId, DWORD dwMarkType, LPCTSTR pszDesc, LPTSTR *ppszText, DWORD dwTextLen)
  691. {
  692. LPSTR lpszFmt = NULL;
  693. DWORD dwRC = 0;
  694. switch(SWID(dwId))
  695. {
  696. case SWID_BROWSER_FRAME:
  697. lpszFmt = (LPSTR)c_szBrowserStop;
  698. break;
  699. default:
  700. return(dwRC);
  701. }
  702. if(((DWORD)(lstrlen(lpszFmt) + lstrlen(pszDesc)) - 1) < dwTextLen)
  703. dwRC = wnsprintf(*ppszText, dwTextLen - 1, lpszFmt, pszDesc);
  704. else
  705. StrCpyN(*ppszText, TEXT("ERROR:Desc too long!"), dwTextLen -1);
  706. return(dwRC);
  707. }
  708. #define STARTCAPALL 1
  709. #define STOPCAPALL 2
  710. #define iStartCAPAll() CallICAP(STARTCAPALL)
  711. #define iStopCAPAll() CallICAP(STOPCAPALL)
  712. //===========================================================================================
  713. //===========================================================================================
  714. VOID CallICAP(DWORD dwFunc)
  715. {
  716. if(g_pswi->hModICAP == NULL)
  717. {
  718. if((g_pswi->hModICAP = LoadLibrary("icap.dll")) != NULL)
  719. {
  720. g_pswi->pfnStartCAPAll = (PFN_ICAP) GetProcAddress(g_pswi->hModICAP, "StartCAPAll");
  721. g_pswi->pfnStopCAPAll = (PFN_ICAP) GetProcAddress(g_pswi->hModICAP, "StopCAPAll");
  722. }
  723. else
  724. {
  725. g_pswi->hModICAP = (HMODULE)1;
  726. }
  727. }
  728. switch(dwFunc)
  729. {
  730. case STARTCAPALL:
  731. if(g_pswi->pfnStartCAPAll != NULL)
  732. g_pswi->pfnStartCAPAll();
  733. break;
  734. case STOPCAPALL:
  735. if(g_pswi->pfnStopCAPAll != NULL)
  736. g_pswi->pfnStopCAPAll();
  737. break;
  738. }
  739. }
  740. //===========================================================================================
  741. //===========================================================================================
  742. VOID CapBreak(BOOL fStart)
  743. {
  744. if((g_pswi->dwStopWatchMode & SPMODE_PROFILE) || (g_pswi->pHtmPerfCtl->dwFlags & HTMPF_ENABLE_PROFILE))
  745. {
  746. if(fStart)
  747. iStartCAPAll();
  748. else
  749. iStopCAPAll();
  750. }
  751. if((g_pswi->dwStopWatchMode & SPMODE_MEMWATCH) || (g_pswi->pHtmPerfCtl->dwFlags & HTMPF_ENABLE_MEMWATCH))
  752. {
  753. if(g_pswi->hModMemWatch == NULL)
  754. DoMemWatchConfig();
  755. if(fStart)
  756. {
  757. if(g_pswi->pfnMemWatchBegin != NULL)
  758. {
  759. g_pswi->pfnMemWatchBegin(TRUE, FALSE); // synchronous and don't use timer
  760. }
  761. }
  762. else
  763. {
  764. if(g_pswi->pfnMemWatchSnapShot != NULL)
  765. {
  766. g_pswi->pfnMemWatchSnapShot();
  767. }
  768. if(g_pswi->pfnMemWatchEnd != NULL)
  769. {
  770. CHAR szOutFile[MAX_PATH];
  771. DWORD dwLen;
  772. HRESULT hr;
  773. #if STOPWATCH_DEBUG
  774. CHAR szDbg[256];
  775. #endif
  776. #ifndef UNIX
  777. *szOutFile = '\0';
  778. GetWindowsDirectoryA(szOutFile, ARRAYSIZE(szOutFile) - 1);
  779. dwLen = lstrlenA(szOutFile);
  780. if ((dwLen > 0) && (szOutFile[dwLen-1] == '\\')) // See if windows is installed in the root
  781. szOutFile[dwLen-1] = '\0';
  782. StrNCatA(szOutFile, "\\shperf.mws", ARRAYSIZE(szOutFile) - 1);
  783. #else
  784. StrCpyNA(szOutFile, ARRAYSIZE(szOutFile) - 1, "shperf.mws");
  785. #endif
  786. hr = g_pswi->pfnMemWatchEnd(szOutFile);
  787. #if STOPWATCH_DEBUG
  788. switch(hr)
  789. {
  790. case E_FAIL:
  791. wnsprintfA(szDbg, ARRAYSIZE(szDbg) - 1, "MemWatch SaveBuffer:%s failed. GLE:0x%x\n", szOutFile, GetLastError());
  792. OutputDebugStringA(szDbg);
  793. break;
  794. case E_ABORT:
  795. wnsprintfA(szDbg, ARRAYSIZE(szDbg) - 1, "MemWatch SaveBuffer: No data to save.\n");
  796. OutputDebugStringA(szDbg);
  797. break;
  798. }
  799. #endif
  800. if(g_pswi->pfnMemWatchExit != NULL)
  801. g_pswi->pfnMemWatchExit();
  802. }
  803. }
  804. }
  805. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGBREAK)
  806. {
  807. DebugBreak();
  808. }
  809. }
  810. //===========================================================================================
  811. // Function: DWORD WINAPI StopWatch(
  812. // DWORD dwId, // The unique identifier, SWID_*, used to associate start, lap, and
  813. // // stop timings for a given timing sequence.
  814. // LPCSTR pszDesc, // Descriptive text for the timing.
  815. // DWORD dwMarkType, // START_NODE, LAP_NODE, STOP_NODE
  816. // DWORD dwFlags, // SPMODE_SHELL, SPMODE_DEBUGOUT, SPMODE_*. The timing call is used
  817. // only if g_pswi->dwStopWatchMode contains dwFlags
  818. // DWORD dwID) // Unique ID (Thread ID or user-supplied value)
  819. //
  820. // Macros: StopWatch_Start(dwId, pszDesc, dwFlags)
  821. // StopWatch_Lap(dwId, pszDesc, dwFlags)
  822. // StopWatch_Stop(dwId, pszDesc, dwFlags)
  823. //
  824. // Returns: ERROR_SUCCESS or ERROR_NOT_ENOUGH_MEMORY if out of nodes
  825. //===========================================================================================
  826. DWORD _StopWatch(DWORD dwId, LPCTSTR pszDesc, DWORD dwMarkType, DWORD dwFlags, DWORD dwCount, DWORD dwUniqueID)
  827. {
  828. PSTOPWATCH psp;
  829. #ifdef STOPWATCH_DEBUG
  830. PSTOPWATCH pspPrev;
  831. #endif
  832. DWORD dwDelta = 0;
  833. DWORD dwRC = ERROR_SUCCESS;
  834. DWORD dwIndex;
  835. TCHAR szText[STOPWATCH_MAX_DESC];
  836. LPTSTR psz;
  837. if((SWID(dwId) && g_pswi->dwStopWatchProfile) && (dwMarkType == STOP_NODE))
  838. {
  839. CapBreak(FALSE);
  840. }
  841. if((g_pswi->pStopWatchList != NULL) && ((dwFlags & g_pswi->dwStopWatchMode) & SPMODES))
  842. {
  843. ENTERCRITICAL;
  844. dwIndex = g_pswi->dwStopWatchListIndex++;
  845. LEAVECRITICAL;
  846. if(dwIndex < (g_pswi->dwStopWatchListMax-1))
  847. {
  848. psp = g_pswi->pStopWatchList + (dwIndex);
  849. psp->dwCount = (dwCount != 0 ?dwCount :GetPerfTime()); // Save the data
  850. psp->dwId = dwId;
  851. psp->dwTID = dwUniqueID ? dwUniqueID : GetCurrentThreadId();
  852. psp->dwType = dwMarkType;
  853. psp->dwFlags = dwFlags;
  854. psz = (LPTSTR)pszDesc;
  855. if(dwFlags & SPMODE_FORMATTEXT)
  856. {
  857. psz = (LPTSTR)szText;
  858. MakeStopWatchDesc(dwId, dwMarkType, pszDesc, &psz, ARRAYSIZE(szText));
  859. }
  860. StrCpyN(psp->szDesc, psz, ARRAYSIZE(psp->szDesc)-1);
  861. if((g_pswi->dwStopWatchMode & SPMODE_PERFTAGS) && (g_pswi->pfnPerfLogFn != NULL))
  862. {
  863. if(dwMarkType == START_NODE)
  864. g_pswi->pfnPerfLogFn(g_pswi->tagStopWatchStart, IntToPtr(dwId), psz);
  865. if(dwMarkType == STOP_NODE)
  866. g_pswi->pfnPerfLogFn(g_pswi->tagStopWatchStop, IntToPtr(dwId), psz);
  867. if(dwMarkType == LAP_NODE)
  868. g_pswi->pfnPerfLogFn(g_pswi->tagStopWatchLap, IntToPtr(dwId), psz);
  869. }
  870. #ifdef STOPWATCH_DEBUG
  871. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  872. {
  873. const TCHAR c_szFmt_StopWatch_DbgOut[] = TEXT("StopWatch: 0x%x: %s: Time: %u ms\r\n");
  874. TCHAR szBuf[STOPWATCH_MAX_DESC + ARRAYSIZE(c_szFmt_StopWatch_DbgOut) + 40]; // 8=dwTID 10=dwDelta
  875. if(psp->dwType > START_NODE) // Find the previous associated node to get delta time
  876. {
  877. pspPrev = psp - 1;
  878. while(pspPrev >= g_pswi->pStopWatchList)
  879. {
  880. if((SWID(pspPrev->dwId) == SWID(psp->dwId)) && // Found a match
  881. (pspPrev->dwTID == psp->dwTID) &&
  882. (pspPrev->dwType == START_NODE))
  883. {
  884. dwDelta = psp->dwCount - pspPrev->dwCount;
  885. break;
  886. }
  887. pspPrev--;
  888. }
  889. }
  890. wnsprintf((LPTSTR)szBuf, ARRAYSIZE(szBuf), c_szFmt_StopWatch_DbgOut, psp->dwTID, psp->szDesc, dwDelta);
  891. OutputDebugString(szBuf);
  892. }
  893. #endif
  894. if((dwMarkType == STOP_NODE) && (g_pswi->dwStopWatchMode & SPMODE_FLUSH) && (SWID(dwId) == SWID_FRAME))
  895. {
  896. StopWatchFlush();
  897. }
  898. }
  899. else
  900. {
  901. psp = g_pswi->pStopWatchList + (g_pswi->dwStopWatchListMax-1); // Set the last node to a message so the user knows we ran out or mem
  902. psp->dwId = 0;
  903. psp->dwType = OUT_OF_NODES;
  904. psp->dwFlags = dwFlags;
  905. wnsprintf(psp->szDesc, STOPWATCH_MAX_DESC, TEXT("Out of perf timing nodes."));
  906. #ifdef STOPWATCH_DEBUG
  907. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  908. OutputDebugString(psp->szDesc);
  909. #endif
  910. dwRC = ERROR_NOT_ENOUGH_MEMORY;
  911. }
  912. }
  913. if((SWID(dwId) && g_pswi->dwStopWatchProfile) && (dwMarkType == START_NODE))
  914. {
  915. CapBreak(TRUE);
  916. }
  917. return(dwRC);
  918. }
  919. //===========================================================================================
  920. //===========================================================================================
  921. DWORD WINAPI StopWatchA(DWORD dwId, LPCSTR pszDesc, DWORD dwMarkType, DWORD dwFlags, DWORD dwCount)
  922. {
  923. #ifdef UNICODE
  924. INT rc;
  925. WCHAR wszDesc[STOPWATCH_MAX_DESC];
  926. rc = MultiByteToWideChar(CP_ACP, 0, pszDesc, -1, wszDesc, STOPWATCH_MAX_DESC);
  927. if(!rc)
  928. return(ERROR_NOT_ENOUGH_MEMORY);
  929. return(_StopWatch(dwId, (LPCTSTR)wszDesc, dwMarkType, dwFlags, dwCount, 0));
  930. #else
  931. return(_StopWatch(dwId, (LPCTSTR)pszDesc, dwMarkType, dwFlags, dwCount, 0));
  932. #endif
  933. }
  934. //===========================================================================================
  935. //===========================================================================================
  936. DWORD WINAPI StopWatchW(DWORD dwId, LPCWSTR pwszDesc, DWORD dwMarkType, DWORD dwFlags, DWORD dwCount)
  937. {
  938. #ifndef UNICODE
  939. INT rc;
  940. CHAR szDesc[STOPWATCH_MAX_DESC];
  941. rc = WideCharToMultiByte(CP_ACP, 0, pwszDesc, -1, szDesc, STOPWATCH_MAX_DESC, NULL, NULL);
  942. if(!rc)
  943. return(ERROR_NOT_ENOUGH_MEMORY);
  944. return(_StopWatch(dwId, (LPCTSTR)szDesc, dwMarkType, dwFlags, dwCount, 0));
  945. #else
  946. return(_StopWatch(dwId, (LPCTSTR)pwszDesc, dwMarkType, dwFlags, dwCount, 0));
  947. #endif
  948. }
  949. //===========================================================================================
  950. //===========================================================================================
  951. DWORD WINAPI StopWatchExA(DWORD dwId, LPCSTR pszDesc, DWORD dwMarkType, DWORD dwFlags, DWORD dwCount, DWORD dwCookie)
  952. {
  953. #ifdef UNICODE
  954. INT rc;
  955. WCHAR wszDesc[STOPWATCH_MAX_DESC];
  956. rc = MultiByteToWideChar(CP_ACP, 0, pszDesc, -1, wszDesc, STOPWATCH_MAX_DESC);
  957. if(!rc)
  958. return(ERROR_NOT_ENOUGH_MEMORY);
  959. return(_StopWatch(dwId, (LPCTSTR)wszDesc, dwMarkType, dwFlags, dwCount, dwCookie));
  960. #else
  961. return(_StopWatch(dwId, (LPCTSTR)pszDesc, dwMarkType, dwFlags, dwCount, dwCookie));
  962. #endif
  963. }
  964. //===========================================================================================
  965. //===========================================================================================
  966. DWORD WINAPI StopWatchExW(DWORD dwId, LPCWSTR pwszDesc, DWORD dwMarkType, DWORD dwFlags, DWORD dwCount, DWORD dwCookie)
  967. {
  968. #ifndef UNICODE
  969. INT rc;
  970. CHAR szDesc[STOPWATCH_MAX_DESC];
  971. rc = WideCharToMultiByte(CP_ACP, 0, pwszDesc, -1, szDesc, STOPWATCH_MAX_DESC, NULL, NULL);
  972. if(!rc)
  973. return(ERROR_NOT_ENOUGH_MEMORY);
  974. return(_StopWatch(dwId, (LPCTSTR)szDesc, dwMarkType, dwFlags, dwCount, dwCookie));
  975. #else
  976. return(_StopWatch(dwId, (LPCTSTR)pwszDesc, dwMarkType, dwFlags, dwCount, dwCookie));
  977. #endif
  978. }
  979. //===========================================================================================
  980. // Function: DWORD WINAPI StopWatchFlush(VOID)
  981. //
  982. // This function will flush any SPMODE_SHELL nodes to windir\shperf.log. Calling this function
  983. // will also clear all nodes.
  984. //
  985. // Return: ERROR_SUCCESS if the log file was generated
  986. // ERROR_NO_DATA if the timing array is empty
  987. // ERROR_INVALID_DATA if stopwatch mode is not enabled or the timing array does
  988. // not exist.
  989. //===========================================================================================
  990. DWORD WINAPI StopWatchFlush(VOID)
  991. {
  992. PSTOPWATCH psp;
  993. PSTOPWATCH psp1 = NULL;
  994. BOOL fWroteStartData;
  995. DWORD dwRC = ERROR_SUCCESS;
  996. DWORD dwWritten;
  997. DWORD dwDelta;
  998. DWORD dwPrevCount;
  999. DWORD dwCummDelta;
  1000. DWORD dwLen = 0;
  1001. HANDLE hFile;
  1002. SYSTEMTIME st;
  1003. TCHAR szBuf[STOPWATCH_MAX_BUF];
  1004. TCHAR szFileName[MAX_PATH];
  1005. #ifdef STOPWATCH_DEBUG
  1006. TCHAR szDbg[512];
  1007. #endif
  1008. if((!g_pswi->dwStopWatchMode) || (g_pswi->pStopWatchList == NULL))
  1009. {
  1010. SetLastError(ERROR_INVALID_DATA);
  1011. return(ERROR_INVALID_DATA);
  1012. }
  1013. GetSystemTime(&st);
  1014. if(g_pswi->dwStopWatchListIndex > 0)
  1015. {
  1016. ENTERCRITICAL;
  1017. if(g_pswi->dwStopWatchListIndex > 0)
  1018. {
  1019. g_pswi->dwStopWatchListIndex = 0;
  1020. if(g_pswi->dwStopWatchMode & SPMODES)
  1021. {
  1022. #ifdef STOPWATCH_DEBUG
  1023. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1024. OutputDebugString(TEXT("Flushing shell perf data to shperf.log\r\n"));
  1025. #endif
  1026. #ifndef UNIX
  1027. // Used below as well to create msg trace log file
  1028. dwLen = GetWindowsDirectory(szFileName, MAX_PATH);
  1029. szFileName[dwLen] = 0;
  1030. if(dwLen && szFileName[dwLen-1] == TEXT('\\')) // See if windows is installed in the root
  1031. szFileName[dwLen-1] = TEXT('\0');
  1032. StrNCat(szFileName, TEXT("\\shperf.log"), ARRAYSIZE(szFileName) -1);
  1033. #else
  1034. StrCpyN(szFileName, TEXT("shperf.log"), ARRAYSIZE(szFileName)-1);
  1035. #endif
  1036. if((hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
  1037. {
  1038. SetFilePointer(hFile, 0, NULL, FILE_END);
  1039. psp = g_pswi->pStopWatchList;
  1040. while(psp->dwType != EMPTY_NODE)
  1041. {
  1042. #ifdef STOPWATCH_DEBUG
  1043. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1044. {
  1045. wnsprintf(szDbg, ARRAYSIZE(szDbg), TEXT("ID:%d TID:0x%x Type:%d Flgs:%d %s\r\n"),
  1046. psp->dwId, psp->dwTID, psp->dwType, psp->dwFlags, psp->szDesc);
  1047. OutputDebugString(szDbg);
  1048. }
  1049. #endif
  1050. if(psp->dwType == START_NODE)
  1051. {
  1052. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%02d%02d%02d%02d%02d%02d\t0x%x\t%s\t%lu\t"),
  1053. st.wYear, st.wMonth, st.wDay,
  1054. st.wHour, st.wMinute, st.wSecond,
  1055. psp->dwId, psp->szDesc, psp->dwCount);
  1056. #ifdef STOPWATCH_DEBUG
  1057. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1058. OutputDebugString(TEXT("Found Start Node\r\n"));
  1059. #endif
  1060. dwDelta = dwCummDelta = 0;
  1061. dwPrevCount = psp->dwCount;
  1062. psp1 = psp + 1;
  1063. fWroteStartData = FALSE;
  1064. while(psp1->dwType != EMPTY_NODE)
  1065. {
  1066. #ifdef STOPWATCH_DEBUG
  1067. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1068. {
  1069. wnsprintf(szDbg, ARRAYSIZE(szDbg), TEXT(" ID:%d TID:0x%x Type:%d Flgs:%d %s\r\n"),
  1070. psp1->dwId, psp1->dwTID, psp1->dwType, psp1->dwFlags, psp1->szDesc);
  1071. OutputDebugString(szDbg);
  1072. }
  1073. #endif
  1074. if((SWID(psp1->dwId) == SWID(psp->dwId)) &&
  1075. (psp1->dwTID == psp->dwTID)) // Found a matching LAP or STOP node
  1076. {
  1077. if(psp1->dwType != START_NODE)
  1078. {
  1079. dwDelta = psp1->dwCount - dwPrevCount;
  1080. dwCummDelta += dwDelta;
  1081. if(!fWroteStartData)
  1082. {
  1083. fWroteStartData = TRUE;
  1084. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL); // Write out start node data
  1085. }
  1086. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s\t%lu,%lu,%lu\t"), psp1->szDesc, psp1->dwCount, dwDelta, dwCummDelta);
  1087. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL);
  1088. #ifdef STOPWATCH_DEBUG
  1089. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1090. OutputDebugString(TEXT(" Found Lap/Stop Node\r\n"));
  1091. #endif
  1092. dwPrevCount = psp1->dwCount;
  1093. if(psp1->dwType == STOP_NODE && !(g_pswi->dwStopWatchMode & SPMODE_MARS))
  1094. break;
  1095. }
  1096. else // We have another start node that matches our Id/TID and we haven't had a stop. Log as a missing stop.
  1097. {
  1098. if(!fWroteStartData)
  1099. {
  1100. fWroteStartData = TRUE;
  1101. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL); // Write out start node data
  1102. }
  1103. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("ERROR: missing stop time"), psp1->szDesc, psp1->dwCount, dwDelta, dwCummDelta);
  1104. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL);
  1105. break;
  1106. }
  1107. }
  1108. psp1++;
  1109. }
  1110. WriteFile(hFile, TEXT("\r\n"), 2, &dwWritten, NULL);
  1111. }
  1112. else if(psp->dwType == OUT_OF_NODES)
  1113. {
  1114. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%02d%02d%02d%02d%02d%02d\t0x%x\t%s\n"),
  1115. st.wYear, st.wMonth, st.wDay,
  1116. st.wHour, st.wMinute, st.wSecond,
  1117. psp->dwId, psp->szDesc);
  1118. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL);
  1119. }
  1120. psp->dwType = EMPTY_NODE;
  1121. psp++;
  1122. }
  1123. FlushFileBuffers(hFile);
  1124. CloseHandle(hFile);
  1125. }
  1126. else
  1127. {
  1128. #ifdef STOPWATCH_DEBUG
  1129. wnsprintf(szBuf, ARRAYSIZE(szBuf) - 1, TEXT("CreateFile failed on '%s'. GLE=%d\n"), szFileName, GetLastError());
  1130. OutputDebugString(szBuf);
  1131. #endif
  1132. dwRC = ERROR_NO_DATA;
  1133. }
  1134. }
  1135. else // !(g_pswi->dwStopWatchMode)
  1136. {
  1137. psp = g_pswi->pStopWatchList;
  1138. while(psp->dwType != EMPTY_NODE)
  1139. {
  1140. psp->dwType = EMPTY_NODE;
  1141. psp++;
  1142. }
  1143. }
  1144. } // (g_pswi->dwStopWatchListIndex > 0)
  1145. LEAVECRITICAL;
  1146. }
  1147. if(g_pswi->dwStopWatchMode & SPMODE_MSGTRACE)
  1148. {
  1149. int i;
  1150. #ifndef UNIX
  1151. lstrcpy(&szFileName[dwLen], TEXT("\\msgtrace.log"));
  1152. #else
  1153. StrCpy(szFileName, TEXT("msgtrace.log"));
  1154. #endif
  1155. if((hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
  1156. {
  1157. SetFilePointer(hFile, 0, NULL, FILE_END);
  1158. for (i = 0; i < (int)(g_pswi->dwStopWatchMaxMsgTime / g_pswi->dwStopWatchMsgInterval); ++i)
  1159. {
  1160. wnsprintf(szBuf, ARRAYSIZE(szBuf) - 1, TEXT("%02d%02d%02d%02d%02d%02d\tMsgTrace\t%4d - %4dms\t%d\r\n"),
  1161. st.wYear, st.wMonth, st.wDay,
  1162. st.wHour, st.wMinute, st.wSecond,
  1163. i * g_pswi->dwStopWatchMsgInterval, (i+1)*g_pswi->dwStopWatchMsgInterval-1, *(g_pswi->pdwStopWatchMsgTime + i));
  1164. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL);
  1165. #ifdef STOPWATCH_DEBUG
  1166. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1167. OutputDebugString(szBuf);
  1168. #endif
  1169. }
  1170. wnsprintf(szBuf, ARRAYSIZE(szBuf) - 1, TEXT("%02d%02d%02d%02d%02d%02d\tMsgTrace\tmsgs >= %dms\t%d\r\n"),
  1171. st.wYear, st.wMonth, st.wDay,
  1172. st.wHour, st.wMinute, st.wSecond,
  1173. g_pswi->dwStopWatchMaxMsgTime, g_pswi->dwcStopWatchOverflow);
  1174. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL);
  1175. #ifdef STOPWATCH_DEBUG
  1176. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1177. OutputDebugString(szBuf);
  1178. #endif
  1179. if(g_pswi->dwStopWatchTraceMsg > 0)
  1180. {
  1181. wnsprintf(szBuf, ARRAYSIZE(szBuf) - 1, TEXT("%02d%02d%02d%02d%02d%02d\tMsgTrace\tmsg 0x%x occured %d times.\r\n"),
  1182. st.wYear, st.wMonth, st.wDay,
  1183. st.wHour, st.wMinute, st.wSecond,
  1184. g_pswi->dwStopWatchTraceMsg, g_pswi->dwStopWatchTraceMsgCnt);
  1185. WriteFile(hFile, szBuf, lstrlen(szBuf), &dwWritten, NULL);
  1186. #ifdef STOPWATCH_DEBUG
  1187. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1188. OutputDebugString(szBuf);
  1189. #endif
  1190. }
  1191. FlushFileBuffers(hFile);
  1192. CloseHandle(hFile);
  1193. }
  1194. else
  1195. {
  1196. #ifdef STOPWATCH_DEBUG
  1197. wnsprintf(szBuf, ARRAYSIZE(szBuf) - 1, TEXT("CreateFile failed on '%s'. GLE=%d\n"), szFileName, GetLastError());
  1198. OutputDebugString(szBuf);
  1199. #endif
  1200. dwRC = ERROR_NO_DATA;
  1201. }
  1202. }
  1203. return(dwRC);
  1204. }
  1205. //===========================================================================================
  1206. // The following StopWatch messages are used to drive the timer msg handler. The timer proc is used
  1207. // as a means of delaying while watching paint messages. If the defined number of timer ticks has
  1208. // passed without getting any paint messages, then we mark the time of the last paint message we've
  1209. // saved as the stop time.
  1210. //===========================================================================================
  1211. VOID CALLBACK StopWatch_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  1212. {
  1213. StopWatch_TimerHandler(hwnd, 1, SWMSG_TIMER, NULL);
  1214. }
  1215. //===========================================================================================
  1216. //===========================================================================================
  1217. BOOL WINAPI StopWatch_TimerHandler(HWND hwnd, UINT uInc, DWORD dwFlag, MSG* pmsg)
  1218. {
  1219. static INT iNumTimersRcvd = 0;
  1220. static DWORD dwCnt = 0;
  1221. static BOOL bActive = FALSE;
  1222. static BOOL bHaveFirstPaintMsg = FALSE;
  1223. switch(dwFlag)
  1224. {
  1225. case SWMSG_PAINT:
  1226. if(bActive)
  1227. {
  1228. dwCnt = GetPerfTime(); // Save tick for last paint message
  1229. iNumTimersRcvd = 0; // Reset timers received count
  1230. if(!bHaveFirstPaintMsg)
  1231. {
  1232. TCHAR szClassName[40]; // If the class matches and its the first paint msg mark a lap time
  1233. LPCTSTR ptr;
  1234. GetClassName(pmsg->hwnd, szClassName, ARRAYSIZE(szClassName)-1);
  1235. ptr = g_pswi->pszClassNames;
  1236. while(*ptr)
  1237. {
  1238. if(lstrcmpi(szClassName, ptr) == 0)
  1239. {
  1240. bHaveFirstPaintMsg = TRUE;
  1241. StopWatch_LapTimed(SWID_FRAME, TEXT("Shell Frame 1st Paint"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwCnt);
  1242. break;
  1243. }
  1244. ptr = ptr + (lstrlen(ptr) + 1);
  1245. }
  1246. }
  1247. }
  1248. break;
  1249. case SWMSG_TIMER:
  1250. iNumTimersRcvd += uInc;
  1251. if(iNumTimersRcvd >= 3) // If we've received this arbitrary # of timer msgs, mark stop time using the saved last paint tick count
  1252. {
  1253. const TCHAR c_szFmtShellStop[] = TEXT("Shell Frame Stop (%s)");
  1254. TCHAR szTitle[STOPWATCH_MAX_TITLE];
  1255. TCHAR szText[ARRAYSIZE(c_szFmtShellStop) + STOPWATCH_MAX_TITLE + 1];
  1256. KillTimer(hwnd, ID_STOPWATCH_TIMER);
  1257. GetWindowText(hwnd, szTitle, ARRAYSIZE(szTitle)-1);
  1258. wnsprintf(szText, ARRAYSIZE(szText), c_szFmtShellStop, szTitle);
  1259. StopWatch_StopTimed(SWID_FRAME, szText, SPMODE_SHELL | SPMODE_DEBUGOUT, dwCnt);
  1260. bHaveFirstPaintMsg = FALSE;
  1261. bActive = FALSE; // Done timing
  1262. if((g_pswi->dwStopWatchMode & (SPMODE_EVENT | SPMODE_SHELL)) == (SPMODE_EVENT | SPMODE_SHELL))
  1263. {
  1264. StopWatch_SignalEvent();
  1265. }
  1266. }
  1267. break;
  1268. case SWMSG_CREATE:
  1269. dwCnt = GetPerfTime(); // Save initial tick in case we don't have a paint when we exceed the # of SWMSG_TIMER above
  1270. iNumTimersRcvd = 0;
  1271. bHaveFirstPaintMsg = FALSE;
  1272. bActive = (BOOL)SetTimer(hwnd, ID_STOPWATCH_TIMER, g_pswi->dwStopWatchPaintInterval, StopWatch_TimerProc); // Use timer to determine when painting is done
  1273. break;
  1274. case SWMSG_STATUS:
  1275. break;
  1276. }
  1277. return(bActive); // Timing status active or not
  1278. }
  1279. //===========================================================================================
  1280. // This function is used to key off of WM_KEYDOWN to start timing when navigating inplace
  1281. //===========================================================================================
  1282. VOID WINAPI StopWatch_CheckMsg(HWND hwnd, MSG msg, LPCSTR lpStr)
  1283. {
  1284. TCHAR szText[80];
  1285. #ifdef STOPWATCH_DEBUG
  1286. if(g_pswi->dwStopWatchMode & SPMODE_TEST) // Used to verify message assumptions
  1287. {
  1288. wnsprintf((LPTSTR)szText, ARRAYSIZE(szText), TEXT("Hwnd=0x%08x Msg=0x%x\r\n"), msg.hwnd, msg.message);
  1289. OutputDebugString(szText);
  1290. }
  1291. #endif
  1292. if(g_pswi->dwStopWatchMode & SPMODE_SHELL)
  1293. {
  1294. if(!StopWatch_TimerHandler(hwnd, 0, SWMSG_STATUS, &msg) &&
  1295. (((msg.message == WM_KEYDOWN) && (msg.wParam == VK_RETURN)) ||
  1296. ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_BACK)))
  1297. ) // Navigating within the same window
  1298. {
  1299. wnsprintf(szText, ARRAYSIZE(szText), TEXT("Shell Frame Same%s"), lpStr);
  1300. StopWatch_TimerHandler(hwnd, 0, SWMSG_CREATE, &msg);
  1301. StopWatch_Start(SWID_FRAME, szText, SPMODE_SHELL | SPMODE_DEBUGOUT);
  1302. }
  1303. }
  1304. // Compute the time it took to get the message. Then increment approp MsgTime bucket
  1305. if(g_pswi->dwStopWatchMode & SPMODE_MSGTRACE)
  1306. {
  1307. DWORD dwTick = GetTickCount();
  1308. DWORD dwElapsed;
  1309. #ifdef STOPWATCH_DEBUG
  1310. TCHAR szMsg[256];
  1311. #endif
  1312. g_pswi->dwStopWatchLastLocation = 0;
  1313. if(dwTick > msg.time)
  1314. {
  1315. dwElapsed = dwTick - msg.time;
  1316. if(dwElapsed >= g_pswi->dwStopWatchMaxMsgTime)
  1317. {
  1318. ++g_pswi->dwcStopWatchOverflow;
  1319. #ifdef STOPWATCH_DEBUG
  1320. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1321. {
  1322. TCHAR szClassName[40];
  1323. TCHAR szMsgName[20];
  1324. GetClassName(msg.hwnd, szClassName, ARRAYSIZE(szClassName) - 1);
  1325. if(g_pswi->pfnDecodeMessage != NULL)
  1326. StrCpyN(szMsgName, g_pswi->pfnDecodeMessage(msg.message), ARRAYSIZE(szMsgName) - 1);
  1327. else
  1328. wnsprintf(szMsgName, ARRAYSIZE(szMsgName) - 1, "0x%x", msg.message);
  1329. wnsprintf(szMsg, ARRAYSIZE(szMsg) - 1, TEXT("MsgTrace (%s) loc=%d, ms=%d >= %d, hwnd=%x, wndproc=%x, msg=%s, w=%x, l=%x\r\n"),
  1330. szClassName, g_pswi->dwStopWatchLastLocation, dwElapsed, g_pswi->dwStopWatchMaxMsgTime, msg.hwnd, GetClassLongPtr(msg.hwnd, GCLP_WNDPROC), szMsgName, msg.wParam, msg.lParam);
  1331. OutputDebugString(szMsg);
  1332. }
  1333. #endif
  1334. }
  1335. else
  1336. {
  1337. ++(*(g_pswi->pdwStopWatchMsgTime + (dwElapsed / g_pswi->dwStopWatchMsgInterval)));
  1338. }
  1339. }
  1340. if(g_pswi->dwStopWatchTraceMsg == msg.message)
  1341. ++g_pswi->dwStopWatchTraceMsgCnt;
  1342. g_pswi->dwStopWatchLastLocation = 0;
  1343. }
  1344. }
  1345. //===========================================================================================
  1346. //===========================================================================================
  1347. VOID WINAPI StopWatch_SetMsgLastLocation(DWORD dwLast)
  1348. {
  1349. g_pswi->dwStopWatchLastLocation = dwLast;
  1350. }
  1351. //===========================================================================================
  1352. // Logs messages that took longer than g_pswi->dwStopWatchMaxDispatchTime to be dispatched
  1353. //===========================================================================================
  1354. DWORD WINAPI StopWatch_DispatchTime(BOOL fStartTime, MSG msg, DWORD dwStart)
  1355. {
  1356. DWORD dwTime = 0;
  1357. TCHAR szMsg[256];
  1358. if(fStartTime)
  1359. {
  1360. if(g_pswi->dwStopWatchTraceMsg == msg.message)
  1361. CapBreak(TRUE);
  1362. StopWatch(SWID_MSGDISPATCH, TEXT("+Dispatch"), START_NODE, SPMODE_MSGTRACE | SPMODE_DEBUGOUT, dwStart);
  1363. dwTime = GetPerfTime();
  1364. }
  1365. else
  1366. {
  1367. dwTime = GetPerfTime();
  1368. if(g_pswi->dwStopWatchTraceMsg == msg.message)
  1369. CapBreak(FALSE);
  1370. if((dwTime - dwStart) >= g_pswi->dwStopWatchMaxDispatchTime)
  1371. {
  1372. TCHAR szClassName[40];
  1373. TCHAR szMsgName[20];
  1374. GetClassName(msg.hwnd, szClassName, ARRAYSIZE(szClassName) - 1);
  1375. if(g_pswi->pfnDecodeMessage != NULL)
  1376. StrCpyN(szMsgName, g_pswi->pfnDecodeMessage(msg.message), ARRAYSIZE(szMsgName) - 1);
  1377. else
  1378. wnsprintf(szMsgName, ARRAYSIZE(szMsgName) - 1, "0x%x", msg.message);
  1379. wnsprintf(szMsg, ARRAYSIZE(szMsg) - 1, TEXT("-Dispatch (%s) ms=%d > %d, hwnd=%x, wndproc=%x, msg=%s(%x), w=%x, l=%x"),
  1380. szClassName, dwTime - dwStart, g_pswi->dwStopWatchMaxDispatchTime, msg.hwnd, GetClassLongPtr(msg.hwnd, GCLP_WNDPROC), szMsgName, msg.message, msg.wParam, msg.lParam);
  1381. StopWatch(SWID_MSGDISPATCH, szMsg, STOP_NODE, SPMODE_MSGTRACE | SPMODE_DEBUGOUT, dwTime);
  1382. #ifdef STOPWATCH_DEBUG
  1383. if(g_pswi->dwStopWatchMode & SPMODE_DEBUGOUT)
  1384. {
  1385. lstrcat(szMsg, "\n");
  1386. OutputDebugString(szMsg);
  1387. }
  1388. #endif
  1389. }
  1390. }
  1391. return(dwTime);
  1392. }
  1393. //===========================================================================================
  1394. // Mark shell/browser frame creation start time
  1395. //===========================================================================================
  1396. VOID WINAPI StopWatch_MarkFrameStart(LPCSTR lpExplStr)
  1397. {
  1398. TCHAR szText[80];
  1399. DWORD dwTime = GetPerfTime();
  1400. if(g_pswi->dwStopWatchMode & SPMODE_SHELL)
  1401. {
  1402. wnsprintf(szText, ARRAYSIZE(szText), TEXT("Shell Frame Start%s"), lpExplStr);
  1403. StopWatch_StartTimed(SWID_FRAME, szText, SPMODE_SHELL | SPMODE_DEBUGOUT, dwTime);
  1404. }
  1405. if(g_pswi->dwStopWatchMode & SPMODE_BROWSER) // Used to get the start of browser total download time
  1406. {
  1407. StopWatch_LapTimed(SWID_BROWSER_FRAME, TEXT("Thread Start"), SPMODE_BROWSER | SPMODE_DEBUGOUT, dwTime);
  1408. }
  1409. if(g_pswi->dwStopWatchMode & SPMODE_JAVA) // Used to get the start of java applet load time
  1410. {
  1411. StopWatch_StartTimed(SWID_JAVA_APP, TEXT("Java Applet Start"), SPMODE_JAVA | SPMODE_DEBUGOUT, dwTime);
  1412. }
  1413. }
  1414. //===========================================================================================
  1415. // Mark shell/browser navigate in same frame start time
  1416. //===========================================================================================
  1417. VOID WINAPI StopWatch_MarkSameFrameStart(HWND hwnd)
  1418. {
  1419. DWORD dwTime = GetPerfTime();
  1420. if(g_pswi->dwStopWatchMode & SPMODE_SHELL)
  1421. {
  1422. StopWatch_TimerHandler(hwnd, 0, SWMSG_CREATE, NULL);
  1423. StopWatch_StartTimed(SWID_FRAME, TEXT("Shell Frame Same"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwTime);
  1424. }
  1425. if(g_pswi->dwStopWatchMode & SPMODE_BROWSER) // Used to get browser total download time
  1426. {
  1427. StopWatch_StartTimed(SWID_BROWSER_FRAME, TEXT("Browser Frame Same"), SPMODE_BROWSER | SPMODE_DEBUGOUT, dwTime);
  1428. }
  1429. if(g_pswi->dwStopWatchMode & SPMODE_JAVA) // Used to get java applet load time
  1430. {
  1431. StopWatch_StartTimed(SWID_JAVA_APP, TEXT("Java Applet Same"), SPMODE_JAVA | SPMODE_DEBUGOUT, dwTime);
  1432. }
  1433. }
  1434. //===========================================================================================
  1435. // When browser or java perf timing mode is enabled, use "Done" or "Applet Started"
  1436. // in the status bar to get load time.
  1437. //===========================================================================================
  1438. VOID WINAPI StopWatch_MarkJavaStop(LPCSTR lpStringToSend, HWND hwnd, BOOL fChType)
  1439. {
  1440. const TCHAR c_szFmtJavaStop[] = TEXT("Java Applet Stop (%s)");
  1441. TCHAR szTitle[STOPWATCH_MAX_TITLE];
  1442. TCHAR szText[STOPWATCH_MAX_TITLE + ARRAYSIZE(c_szFmtJavaStop) + 1];
  1443. if(g_pswi->dwStopWatchMode & SPMODE_JAVA)
  1444. {
  1445. if((lpStringToSend != NULL) && (lstrncmpW((LPWSTR)lpStringToSend, TEXTW("Applet started"), ARRAYSIZE(TEXTW("Applet started"))) == 0))
  1446. {
  1447. GetWindowText(hwnd, szTitle, ARRAYSIZE(szTitle)-1);
  1448. wnsprintf(szText, ARRAYSIZE(szText), c_szFmtJavaStop, szTitle);
  1449. StopWatch_Stop(SWID_JAVA_APP, szText, SPMODE_SHELL | SPMODE_DEBUGOUT);
  1450. }
  1451. }
  1452. }
  1453. //===========================================================================================
  1454. //===========================================================================================
  1455. DWORD WINAPI GetPerfTime(VOID)
  1456. {
  1457. static __int64 freq;
  1458. __int64 curtime;
  1459. if (!freq)
  1460. QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
  1461. QueryPerformanceCounter((LARGE_INTEGER *)&curtime);
  1462. ASSERT((((curtime * 1000) / freq) >> 32) == 0);
  1463. return (DWORD)((curtime * 1000) / freq);
  1464. }