Leaked source code of windows server 2003
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.

9946 lines
396 KiB

  1. /*++
  2. Copyright (c) 1995-2002 Microsoft Corporation
  3. Module Name:
  4. kernrate.c
  5. Abstract:
  6. This program records the rate of various events over a selected
  7. period of time. It uses the kernel profiling mechanism and iterates
  8. through the available profile sources to produce an overall profile
  9. for the various Kernel or User-Process components.
  10. Usage:
  11. kernrate <commad line options>
  12. Author:
  13. John Vert (jvert) 31-Mar-1995
  14. Revision History:
  15. The original MS version has been extensively modified by Thierry Fevrier and Dan Almosnino.
  16. --*/
  17. // KERNRATE Implementation Notes:
  18. //
  19. // 01/10/2000 - Thierry
  20. // The following code assumes that a kernrate compiled for a specific
  21. // platform, executes and processes perf data only for that platform.
  22. //
  23. // Danalm 02/15/2002:
  24. // - Current code supports Windows 2000 and above, won't run on lower versions
  25. //
  26. // KERNRATE ToDoList:
  27. //
  28. // Thierry 09/30/97:
  29. // - KernRate does not clean the ImageHlp API in case of exceptions. I have
  30. // just added a SymCleanup() call at the normal exit of this program but
  31. // it is not sufficient. We should revisit this one day...
  32. //
  33. // Thierry 07/01/2000:
  34. // - Kernrate and the Kernel Profiling objects code assume that code sections
  35. // that we are profiling are not larger than 4GB.
  36. //
  37. #include "kernrate.h"
  38. VOID
  39. vVerbosePrint(
  40. ULONG Level,
  41. PCCHAR Msg,
  42. ...
  43. )
  44. {
  45. if ( gVerbose & Level ) {
  46. va_list ap;
  47. va_start( ap, Msg );
  48. vfprintf(stderr , Msg, ap );
  49. va_end(ap);
  50. }
  51. return;
  52. } // vVerbosePrint()
  53. BOOL
  54. CtrlcH(
  55. DWORD dwCtrlType
  56. )
  57. {
  58. LARGE_INTEGER DueTime;
  59. if ( dwCtrlType == CTRL_C_EVENT ) {
  60. if(gProfilingDone != TRUE) {
  61. if (gSleepInterval == 0) {
  62. SetEvent(ghDoneEvent);
  63. } else {
  64. DueTime.QuadPart = (ULONGLONG)-1;
  65. NtSetTimer(ghDoneEvent,
  66. &DueTime,
  67. NULL,
  68. NULL,
  69. FALSE,
  70. 0,
  71. NULL);
  72. }
  73. }
  74. else {
  75. //MC
  76. //
  77. // If someone kills kernrate by pressing Ctrl-C we need to detach from the process (if attached to it)
  78. // Otherwise that process will hang. Of course we can't do much if someone kills kernrate externally.
  79. //
  80. if( ghMCLib != NULL){
  81. pfnDetachFromProcess();
  82. FreeLibrary(ghMCLib);
  83. ghMCLib = NULL;
  84. exit(0);
  85. }
  86. //MC
  87. }
  88. return TRUE;
  89. }
  90. return FALSE;
  91. } // CtrlcH()
  92. static VOID
  93. UsageVerbose(
  94. VOID
  95. )
  96. {
  97. PVERBOSE_DEFINITION pdef = VerboseDefinition;
  98. FPRINTF( stderr, " -v [VerboseLevels] Verbose output where VerboseLevels:\n");
  99. while( pdef->VerboseString ) {
  100. FPRINTF( stderr, " - %x %s\n", pdef->VerboseEnum,
  101. pdef->VerboseString
  102. );
  103. pdef++;
  104. }
  105. FPRINTF( stderr, " - Default value: %x\n", VERBOSE_DEFAULT);
  106. FPRINTF( stderr, " These verbose levels can be OR'ed.\n");
  107. return;
  108. } // UsageVerbose()
  109. static VOID
  110. Usage(
  111. BOOL ExitKernrate
  112. )
  113. {
  114. FPRINTF( stderr, "KERNRATE - Version: %s\n", VER_PRODUCTVERSION_STR );
  115. FPRINTF( stderr,
  116. "KERNRATE [-l] [-lx] [-r] [-m] [-p ProcessId] [-z ModuleName] [-j SymbolPath] [-c RateInMsec] [-s Seconds] [-i [SrcShortName] Rate]\n"
  117. " [-n ProcessName] [-w]\n\n"
  118. " -a Do a combined Kernel and User mode profile\n"
  119. " -av Do a combined Kernel and User mode profile and get task list and system threads info\n"
  120. " -b BucketSize Specify profiling bucket size (default = 16 bytes, must be a power of 2)\n"
  121. " -c RateInMsec Change source every N milliseconds (default 1000ms). Optional. By default all sources will be profiled simultaneously\n"
  122. " -d Generate output rounding buckets up and down\n"
  123. " -e Exclude system-wide and process specific general information (context switches, memory usage, etc.)\n"
  124. " -f Process the collected data at high priority (useful on busy systems if the overhead is not an issue)\n"
  125. " -g Rate Get interesting processor-counters statistics (Rate optional in events/hit), output not guarantied\n"
  126. " -i SrcShortName Rate Specify interrupt interval rate (in events/hit)for the source specified by its ShortName, see notes below\n"
  127. " -j SymbolPath Prepend SymbolPath to the default imagehlp search path\n"
  128. " -k MinHitCount Limit the output to modules that have at least MinHitCount hits\n"
  129. " -l List the default interval rates for supported sources\n"
  130. " -lx List the default interval rates for supported sources and then exit\n"
  131. " -m 0xN Generate per-CPU profiles on multi-processor machines, Hex CPU affinity mask optional for profiling on selected processors\n"
  132. " -n ProcessName Monitor process by its name (default limited to first 8 by the same name), multiple usage allowed\n"
  133. " -nv# N ProcessName Monitor up to N processes by the same name, v will print thread info and list of all running processes (optional)\n"
  134. " -o ProcessName {CmdLine}Create and monitor ProcessName (path OK), Command Line parameters optional and must be enclosed in curly brackets\n"
  135. " -ov# N ProcessName { } Create N instances of ProcessName, v will print thread info and list of running processes (optional), {command line} optional\n"
  136. " -pv ProcessId Monitor process by its ProcessId, multiple usage allowed - see notes below, v (optional) same as in '-nv'\n"
  137. " -r Raw data from zoomed modules\n"
  138. " -rd Raw data from zoomed modules with disassembly\n"
  139. " -s Seconds Stop collecting data after N seconds\n"
  140. " -t Display process list + CPU usage summary for the profiling period\n"
  141. " -t MaxTasks As above + Change the maximum no. of processes allowed in Kernrate's list to MaxTasks (default: 256)\n"
  142. " -u Present symbols in undecorated form\n"
  143. " -w Wait for the user to press ENTER before starting to collect profile data\n"
  144. " -w Seconds Wait for N seconds before starting to collect profile data (default is no wait)\n"
  145. " -wp Wait for the user to press enter to indicate that created processes (see -0 option) are settled (idle)\n"
  146. " -wp Seconds Wait for N seconds to allow created processes settle (go idle), default is 2 seconds, (see the -o option)\n"
  147. " -x Get both system and user-process locks information\n"
  148. " -x# count Get both system and user-process locks information for (optional) contention >= count [def. 1000]\n"
  149. " -xk# count Get only system lock information for (optional) contention >= count [def. 1000]\n"
  150. " -xu# count Get only user-process lock information for (optional) contention >= count [def. 1000]\n"
  151. " -z module Name of module to zoom on (no extension needed by default) such as ntdll, multiple usage allowed, see notes below\n"
  152. " -v Verbose Verbose Printout, if specified with no level the default is Imagehlp symbol information\n"
  153. );
  154. UsageVerbose(); // -v switches
  155. FPRINTF( stderr,
  156. "\nMulti-Processes are allowed (each process ID needs to be preceded by -P except for the system process)\n"
  157. "Typical multi-process profiling command line should look like:\n"
  158. "\nkernrate .... -a -z ntoskrnl -z ntdll -z kernel32 -p 1234 -z w3svc -z iisrtl -p 4321 -z comdlg32 -z msvcrt ...\n"
  159. "\nThe first group of -z denotes either kernel modules and-or modules common across processes\n"
  160. "The other -z groups are process specific and should always follow the appropriate -p xxx \n"
  161. "\nThe -z option requires to add the extension (.dll etc.) only if two or more binaries carry the same name and differ only by the extension\n"
  162. "\nThe '-g' option will attempt to turn on multiple sources. One source at a time profiling mode will automatically be forced\n"
  163. "\nThe '-i' option can be followed by only a source name (system default interrupt interval rate will then be assumed)\n"
  164. "\nA '-i' option followed by a rate amount (no profile source name) will change the interval rate for the default source (time)\n"
  165. "\nProfiling of the default source (Time) can be disabled by setting its profile interval to zero\n"
  166. "\nWith the '-n' option, use the common modules -Z option if you expect more than one process with the same name\n"
  167. "\nThe '-c' option will cause elapsed time to be devided equally between the sources and the monitored processes\n"
  168. "\nThe '-o' option supports redirection of input/output/error streams within the curly brackets. Each redirection character must be escaped with a '^' character\n"
  169. "----------------------------------------------------------------------------------------------------------------------------\n\n"
  170. );
  171. if(ExitKernrate){
  172. exit(1);
  173. } else {
  174. return;
  175. }
  176. } // Usage()
  177. VOID
  178. CreateDoneEvent(
  179. VOID
  180. )
  181. {
  182. LARGE_INTEGER DueTime;
  183. NTSTATUS Status;
  184. DWORD Error;
  185. if (gSleepInterval == 0) {
  186. //
  187. // Create event that will indicate the test is complete.
  188. //
  189. ghDoneEvent = CreateEvent(NULL,
  190. TRUE,
  191. FALSE,
  192. NULL);
  193. if (ghDoneEvent == NULL) {
  194. Error = GetLastError();
  195. FPRINTF(stderr, "CreateEvent failed %d\n",Error);
  196. exit(Error);
  197. }
  198. } else {
  199. //
  200. // Create timer that will indicate the test is complete
  201. //
  202. Status = NtCreateTimer(&ghDoneEvent,
  203. MAXIMUM_ALLOWED,
  204. NULL,
  205. NotificationTimer);
  206. if (!NT_SUCCESS(Status)) {
  207. FPRINTF(stderr, "NtCreateTimer failed %08lx\n",Status);
  208. exit(Status);
  209. }
  210. DueTime.QuadPart = (LONGLONG) UInt32x32To64(gSleepInterval, 10000);
  211. DueTime.QuadPart *= -1;
  212. Status = NtSetTimer(ghDoneEvent,
  213. &DueTime,
  214. NULL,
  215. NULL,
  216. FALSE,
  217. 0,
  218. NULL);
  219. if (!NT_SUCCESS(Status)) {
  220. FPRINTF(stderr, "NtSetTimer failed %08lx\n",Status);
  221. exit(Status);
  222. }
  223. }
  224. } // CreateDoneEvent()
  225. /* BEGIN_IMS SymbolCallbackFunction
  226. ******************************************************************************
  227. ****
  228. **** SymbolCallbackFunction ( )
  229. ****
  230. ******************************************************************************
  231. *
  232. * Function Description:
  233. *
  234. * The user function is called by IMAGEHLP at the specified operations.
  235. * Refer to the CBA_xxx values.
  236. *
  237. * Arguments:
  238. *
  239. * HANDLE hProcess :
  240. *
  241. * ULONG ActionCode :
  242. *
  243. * PVOID CallbackData :
  244. *
  245. * PVOID UserContext :
  246. *
  247. * Return Value:
  248. *
  249. * BOOL
  250. *
  251. * Algorithm:
  252. *
  253. * ToBeSpecified
  254. *
  255. * Globals Referenced:
  256. *
  257. * ToBeSpecified
  258. *
  259. * Exception Conditions:
  260. *
  261. * ToBeSpecified
  262. *
  263. * In/Out Conditions:
  264. *
  265. * ToBeSpecified
  266. *
  267. * Notes:
  268. *
  269. * ToBeSpecified
  270. *
  271. * ToDo List:
  272. *
  273. * ToBeSpecified
  274. *
  275. * Modification History:
  276. *
  277. * 9/30/97 TF Initial version
  278. *
  279. ******************************************************************************
  280. * END_IMS SymbolCallbackFunction */
  281. BOOL
  282. SymbolCallbackFunction(
  283. HANDLE hProcess,
  284. ULONG ActionCode,
  285. ULONG64 CallbackData,
  286. ULONG64 UserContext
  287. )
  288. {
  289. PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
  290. PIMAGEHLP_DUPLICATE_SYMBOL idup;
  291. PIMAGEHLP_CBA_READ_MEMORY prm;
  292. PMODULE *pmodule;
  293. PMODULE module;
  294. ULONG i;
  295. //
  296. // Note: The default return value for this function is FALSE.
  297. //
  298. assert( UserContext );
  299. idsl = (PIMAGEHLP_DEFERRED_SYMBOL_LOAD64) CallbackData;
  300. switch( ActionCode ) {
  301. case CBA_DEBUG_INFO:
  302. VerbosePrint(( VERBOSE_IMAGEHLP, "%s", (LPSTR)CallbackData ));
  303. break;
  304. case CBA_DEFERRED_SYMBOL_LOAD_START:
  305. if(UserContext){
  306. pmodule = (PMODULE *)UserContext;
  307. module = *pmodule;
  308. if(module != NULL)
  309. VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: Loading symbols for %s...\n",
  310. module->module_FileName
  311. ));
  312. return TRUE;
  313. }
  314. break;
  315. case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
  316. if (hProcess == SYM_KERNEL_HANDLE &&
  317. idsl->SizeOfStruct >= FIELD_OFFSET(IMAGEHLP_DEFERRED_SYMBOL_LOAD,
  318. Reparse))
  319. {
  320. i = 0;
  321. if (strncmp(idsl->FileName, "dump_", sizeof("dump_")-1) == 0)
  322. {
  323. i = sizeof("dump_")-1;
  324. }
  325. else if (strncmp(idsl->FileName, "hiber_", sizeof("hiber_")-1) == 0)
  326. {
  327. i = sizeof("hiber_")-1;
  328. }
  329. if (i)
  330. {
  331. if (_stricmp (idsl->FileName+i, "scsiport.sys") == 0)
  332. {
  333. strncpy (idsl->FileName, "diskdump.sys", MAX_PATH-1);
  334. idsl->FileName[ MAX_PATH-1 ] = '\0';
  335. }
  336. else
  337. {
  338. strncpy(idsl->FileName, idsl->FileName+i, MAX_PATH-1);
  339. idsl->FileName[ MAX_PATH-1 ] = '\0';
  340. }
  341. idsl->Reparse = TRUE;
  342. return TRUE;
  343. }
  344. }
  345. if (idsl->FileName && *idsl->FileName)
  346. {
  347. VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: could not load symbols for %s\n",
  348. idsl->FileName
  349. ));
  350. }
  351. else
  352. {
  353. VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: could not load symbols [MODNAME UNKNOWN]\n"
  354. ));
  355. }
  356. break;
  357. case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
  358. if(UserContext){
  359. pmodule = (PMODULE *)UserContext;
  360. module = *pmodule;
  361. if(idsl && module)
  362. FPRINTF(stderr, "CallBack: Finished Attempt to Load symbols for %I64x %s\n\n",
  363. idsl->BaseOfImage,
  364. ModuleFullName( module )
  365. );
  366. }
  367. return TRUE;
  368. case CBA_SYMBOLS_UNLOADED:
  369. VerbosePrint(( VERBOSE_IMAGEHLP, "CallBack: Symbols Unloaded.\n" ));
  370. break;
  371. case CBA_DUPLICATE_SYMBOL:
  372. idup = (PIMAGEHLP_DUPLICATE_SYMBOL) CallbackData;
  373. if(UserContext){
  374. pmodule = (PMODULE *)UserContext;
  375. module = *pmodule;
  376. if(module != NULL && module->module_FileName != NULL )
  377. VerbosePrint(( VERBOSE_IMAGEHLP, "Callback: Attempt to load Duplicate symbol for %s\n",
  378. module->module_FileName
  379. ));
  380. }
  381. if(idup != NULL)
  382. FPRINTF( stderr, "*** WARNING: Found %ld duplicate symbols for %s\n",
  383. idup->NumberOfDups,
  384. (idup->SelectedSymbol != (ULONG)-1) ? idup->Symbol[idup->SelectedSymbol].Name : "unknown symbol"
  385. );
  386. return TRUE;
  387. case CBA_READ_MEMORY:
  388. prm = (PIMAGEHLP_CBA_READ_MEMORY) CallbackData;
  389. if(prm != NULL){
  390. return ReadProcessMemory(hProcess,
  391. (LPCVOID)prm->addr,
  392. prm->buf,
  393. prm->bytes,
  394. NULL) == S_OK;
  395. }
  396. break;
  397. default:
  398. return FALSE;
  399. }
  400. return FALSE;
  401. } // SymbolCallBackFunction()
  402. static PCHAR
  403. GetSymOptionsValues( DWORD SymOptions )
  404. {
  405. static CHAR values[SYM_VALUES_BUF_SIZE];
  406. ULONG valuesSize = SYM_VALUES_BUF_SIZE - 1;
  407. values[0] = '\0';
  408. if ( SymOptions & SYMOPT_CASE_INSENSITIVE ) {
  409. (void)strncat( values, "CASE_INSENSITIVE ", valuesSize );
  410. SymOptions &= ~SYMOPT_CASE_INSENSITIVE;
  411. }
  412. if ( SymOptions & SYMOPT_UNDNAME ) {
  413. (void)strncat( values, "UNDNAME ", valuesSize-lstrlen(values) );
  414. SymOptions &= ~SYMOPT_UNDNAME;
  415. }
  416. if ( SymOptions & SYMOPT_DEFERRED_LOADS ) {
  417. (void)strncat( values, "DEFERRED_LOADS ", valuesSize-lstrlen(values) );
  418. SymOptions &= ~SYMOPT_DEFERRED_LOADS;
  419. }
  420. if ( SymOptions & SYMOPT_NO_CPP ) {
  421. (void)strncat( values, "NO_CPP ", valuesSize-lstrlen(values) );
  422. SymOptions &= ~SYMOPT_NO_CPP;
  423. }
  424. if ( SymOptions & SYMOPT_LOAD_LINES ) {
  425. (void)strncat( values, "LOAD_LINES ", valuesSize-lstrlen(values) );
  426. SymOptions &= ~SYMOPT_LOAD_LINES;
  427. }
  428. if ( SymOptions & SYMOPT_OMAP_FIND_NEAREST ) {
  429. (void)strncat( values, "OMAP_FIND_NEAREST ", valuesSize-lstrlen(values) );
  430. SymOptions &= ~SYMOPT_OMAP_FIND_NEAREST;
  431. }
  432. if ( SymOptions & SYMOPT_DEBUG ) {
  433. (void)strncat( values, "DEBUG ", valuesSize-lstrlen(values) );
  434. SymOptions &= ~SYMOPT_DEBUG;
  435. }
  436. if ( SymOptions ) {
  437. CHAR uknValues[10];
  438. (void)_snprintf( uknValues, 10, "0x%x", SymOptions );
  439. (void)strncat( values, uknValues, valuesSize-lstrlen(values) );
  440. }
  441. values[valuesSize] = '\0';
  442. return( values );
  443. } // GetSymOptionsValues()
  444. void __cdecl UInt64Div (
  445. unsigned __int64 numer,
  446. unsigned __int64 denom,
  447. uint64div_t *result
  448. )
  449. {
  450. assert(result);
  451. if ( denom != (unsigned __int64)0 ) {
  452. result->quot = numer / denom;
  453. result->rem = numer % denom;
  454. }
  455. else {
  456. result->rem = result->quot = (unsigned __int64)0;
  457. }
  458. return;
  459. } // UInt64Div()
  460. void __cdecl Int64Div (
  461. __int64 numer,
  462. __int64 denom,
  463. int64div_t *result
  464. )
  465. {
  466. assert(result);
  467. if ( denom != (__int64)0 ) {
  468. result->quot = numer / denom;
  469. result->rem = numer % denom;
  470. if (numer < 0 && result->rem > 0) {
  471. /* did division wrong; must fix up */
  472. ++result->quot;
  473. result->rem -= denom;
  474. }
  475. }
  476. else {
  477. result->rem = result->quot = (__int64)0;
  478. }
  479. return;
  480. } // Int64Div()
  481. unsigned __int64 __cdecl
  482. UInt64PerCent( unsigned __int64 Val, unsigned __int64 Denom )
  483. {
  484. uint64div_t v;
  485. UInt64Div( 100*Val, Denom, &v );
  486. while ( v.rem > UINT64_MAXDWORD ) {
  487. v.quot++;
  488. v.rem -= UINT64_MAXDWORD;
  489. }
  490. return( v.quot );
  491. } // UInt64PerCent()
  492. double
  493. UInt64ToDoublePerCent( unsigned __int64 Val, unsigned __int64 Denom )
  494. {
  495. double retval;
  496. retval = ( Denom > (__int64) 0 )? ((double) (__int64)Val / (double) (__int64)Denom)*(double)100 : (double) 0;
  497. return retval;
  498. }
  499. //////////////////////////////////////////////////
  500. // //
  501. // Main //
  502. // //
  503. //////////////////////////////////////////////////
  504. int
  505. __cdecl
  506. main (
  507. int argc,
  508. char *argv[]
  509. )
  510. {
  511. PPROC_TO_MONITOR ProcToMonitor = NULL;
  512. ULONG i,j;
  513. ULONG NumTasks;
  514. BOOLEAN Enabled;
  515. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoBegin; //For the Profile period only
  516. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoEnd; //For the Profile period only
  517. NTSTATUS Status;
  518. PTASK_LIST tlist;
  519. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StartInfo2; //For the extra system-wide and process specific information
  520. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION StopInfo2; //For the extra system-wide and process specific information
  521. //// Beginning of Program-wide Assertions Section
  522. //
  523. //
  524. //
  525. // This code does not support UNICODE strings
  526. //
  527. #if defined(UNICODE) || defined(_UNICODE)
  528. #error This code does not support UNICODE strings!!!
  529. #endif // UNICODE || _UNICODE
  530. //
  531. //
  532. //// End of Program-wide Assertions Section
  533. //
  534. // Per user request, set priority up to realtime to accelerate initialization and symbol loading,
  535. // minimize timing glitches during the profile and post process the data at high priority
  536. //
  537. if (bProcessDataHighPriority == TRUE ) {
  538. SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  539. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
  540. }
  541. InitializeKernrate( argc, argv );
  542. if(bWaitCreatedProcToSettle == TRUE){
  543. //
  544. // First Check if the user asked to wait for a key press (ENTER) to wait a created processes to settle
  545. //
  546. if(bCreatedProcWaitForUserInput == TRUE){
  547. FPRINTF(stderr, "\n***> Waiting for created processes to settle (go idle) Please press ENTER when ready\n");
  548. getchar();
  549. } else {
  550. //
  551. // Wait for a given number of seconds for created processes to settle
  552. //
  553. FPRINTF(stderr, "\nWaiting for %d seconds to let created processe(s) settle (go idle)\n", gSecondsToWaitCreatedProc);
  554. Sleep(1000*gSecondsToWaitCreatedProc);
  555. }
  556. }
  557. StartInfo2 = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
  558. if (StartInfo2 == NULL) {
  559. FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(1) failed\n");
  560. exit(1);
  561. }
  562. StopInfo2 = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
  563. if (StopInfo2 == NULL) {
  564. FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(2) failed\n");
  565. exit(1);
  566. }
  567. SystemInfoBegin = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
  568. if (SystemInfoBegin == NULL) {
  569. FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(3) failed\n");
  570. exit(1);
  571. }
  572. SystemInfoEnd = calloc(1, gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
  573. if (SystemInfoEnd == NULL) {
  574. FPRINTF(stderr, "KERNRATE: Allocation for SYSTEM_PROCESSOR_PERFORMANCE_INFO(4) failed\n");
  575. exit(1);
  576. }
  577. InitAllProcessesModulesInfo();
  578. //
  579. // Adjust security level to the needed level for system profile
  580. //
  581. Status = RtlAdjustPrivilege(SE_SYSTEM_PROFILE_PRIVILEGE, //If ever not sufficient use SE_DEBUG_PRIVILEGE,
  582. TRUE,
  583. FALSE,
  584. &Enabled
  585. );
  586. if( !NT_SUCCESS(Status) ) {
  587. FPRINTF(stderr,"RtlAdjustPrivilege(SE_PROFILE_PRIVILEGE) failed: %08x\n", Status);
  588. exit(1);
  589. }
  590. //
  591. // Find the number of Active Sources and store the indices of the active sources
  592. // To be used later for better performance than going over the whole source list
  593. //
  594. ProcToMonitor = gProcessList;
  595. for (i=0; i < gSourceMaximum; i++) {
  596. if (ProcToMonitor->Source[i].Interval != 0) {
  597. gTotalActiveSources += 1;
  598. }
  599. }
  600. gulActiveSources = (PULONG)malloc( gTotalActiveSources*sizeof(ULONG) );
  601. if (gulActiveSources==NULL) {
  602. FPRINTF(stderr, "\nMemory allocation failed for ActiveSources in GetConfiguration\n");
  603. exit(1);
  604. }
  605. ProcToMonitor = gProcessList;
  606. j = 0;
  607. for (i=0; i < gSourceMaximum; i++) {
  608. if (ProcToMonitor->Source[i].Interval != 0) {
  609. gulActiveSources[j] = i;
  610. ++j;
  611. }
  612. }
  613. //
  614. // Create necessary profiles for each process
  615. //
  616. ProcToMonitor = gProcessList;
  617. for (i=0; i<gNumProcToMonitor; i++){
  618. if(ProcToMonitor->ModuleList != NULL)
  619. CreateProfiles(ProcToMonitor->ModuleList, ProcToMonitor);
  620. ProcToMonitor = ProcToMonitor->Next;
  621. }
  622. //
  623. // Set priority up to realtime to minimize timing glitches during the profile only
  624. //
  625. if (bProcessDataHighPriority == FALSE ) {
  626. SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  627. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
  628. }
  629. //
  630. // Check if the user asked to wait for a key press (ENTER) before starting the profile
  631. //
  632. if(bWaitForUserInput == TRUE){
  633. FPRINTF(stderr, "\n***> Please press ENTER to start collecting profile data\n");
  634. getchar();
  635. }
  636. //
  637. // Check if the user asked to wait for a given number of seconds before starting the profile
  638. //
  639. if(gSecondsToDelayProfile != 0){
  640. FPRINTF(stderr, "\nWaiting for %d seconds before starting to collect profile data\n", gSecondsToDelayProfile);
  641. Sleep(1000*gSecondsToDelayProfile);
  642. }
  643. FPRINTF(stderr, "Starting to collect profile data\n\n");
  644. if (gSleepInterval == 0) {
  645. FPRINTF(stderr,"***> Press ctrl-c to finish collecting profile data\n");
  646. } else {
  647. FPRINTF(stderr, "Will collect profile data for %d seconds\n", gSleepInterval/1000);
  648. }
  649. //
  650. // Wait for test to complete. Obtain any extra system-wide info out of the profile time-span
  651. //
  652. Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  653. (PVOID)StartInfo2,
  654. gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
  655. NULL);
  656. if (!NT_SUCCESS(Status)) {
  657. FPRINTF(stderr, "Failed to query starting processor performance information %08lx\n",Status);
  658. exit(Status);
  659. }
  660. //
  661. // Get or update task list information
  662. //
  663. if(bIncludeGeneralInfo || bDisplayTaskSummary || (gVerbose & VERBOSE_PROFILING) ) {
  664. if( gTlistStart == NULL ){
  665. gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
  666. if ( gTlistStart == NULL ){
  667. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for the running processes task list(5)\n");
  668. exit(1);
  669. }
  670. }
  671. gNumTasksStart = GetTaskList( gTlistStart, gMaxTasks);
  672. }
  673. if(bIncludeSystemLocksInfo)
  674. GetSystemLocksInformation(START);
  675. if(bIncludeUserProcLocksInfo){
  676. ProcToMonitor = gProcessList;
  677. for (i=0; i<gNumProcToMonitor; i++){
  678. if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE) {
  679. GetProcessLocksInformation( ProcToMonitor,
  680. RTL_QUERY_PROCESS_LOCKS,
  681. START
  682. );
  683. }
  684. ProcToMonitor = ProcToMonitor->Next;
  685. }
  686. }
  687. SetConsoleCtrlHandler(CtrlcH, TRUE);
  688. CreateDoneEvent();
  689. if(bIncludeGeneralInfo)
  690. GetProfileSystemInfo(START);
  691. Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  692. (PVOID)SystemInfoBegin,
  693. gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
  694. NULL);
  695. if (!NT_SUCCESS(Status)) {
  696. FPRINTF(stderr, "Failed to query starting processor performance information %08lx\n",Status);
  697. exit(Status);
  698. }
  699. //
  700. //Execute the actual Profiles
  701. //
  702. ExecuteProfiles( bOldSampling );
  703. gProfilingDone = TRUE; // used to synchronize the ctrl handler.
  704. //
  705. //Obtain end of run system-wide info
  706. //
  707. Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  708. (PVOID)SystemInfoEnd,
  709. gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
  710. NULL);
  711. if (!NT_SUCCESS(Status)) {
  712. FPRINTF(stderr, "Failed to query ending processor performance information %08lx\n",Status);
  713. exit(Status);
  714. }
  715. if(bIncludeGeneralInfo)
  716. GetProfileSystemInfo(STOP);
  717. if(bIncludeUserProcLocksInfo){
  718. ProcToMonitor = gProcessList;
  719. for (i=0; i<gNumProcToMonitor; i++){
  720. if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE) {
  721. GetProcessLocksInformation( ProcToMonitor,
  722. RTL_QUERY_PROCESS_LOCKS,
  723. STOP
  724. );
  725. }
  726. ProcToMonitor = ProcToMonitor->Next;
  727. }
  728. }
  729. if(bIncludeSystemLocksInfo)
  730. GetSystemLocksInformation(STOP);
  731. if(bIncludeGeneralInfo || bDisplayTaskSummary || (gVerbose & VERBOSE_PROFILING) ) {
  732. tlist = calloc(1, gMaxTasks*sizeof(TASK_LIST));
  733. if ( tlist == NULL ){
  734. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for the running processes task list(6)\n");
  735. exit(1);
  736. }
  737. NumTasks = GetTaskList( tlist, gMaxTasks);
  738. gNumTasksStop = NumTasks;
  739. }
  740. Status = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  741. (PVOID)StopInfo2,
  742. gSysBasicInfo->NumberOfProcessors*sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
  743. NULL);
  744. if (!NT_SUCCESS(Status)) {
  745. FPRINTF(stderr, "Failed to query starting processor performance information %08lx\n", Status);
  746. exit(Status);
  747. }
  748. for (i=0; i<(ULONG)gSysBasicInfo->NumberOfProcessors; i++) {
  749. gTotal2ElapsedTime64.QuadPart += ( (StopInfo2[i].UserTime.QuadPart - StartInfo2[i].UserTime.QuadPart) +
  750. (StopInfo2[i].KernelTime.QuadPart - StartInfo2[i].KernelTime.QuadPart) );
  751. }
  752. FPRINTF(stderr, "===> Finished Collecting Data, Starting to Process Results\n");
  753. //
  754. // Reduce priority unless user asked to process the collected data at high priority
  755. //
  756. if (bProcessDataHighPriority == FALSE ) {
  757. SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
  758. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
  759. }
  760. //
  761. // Restore privilege
  762. //
  763. RtlAdjustPrivilege(SE_SYSTEM_PROFILE_PRIVILEGE,
  764. Enabled,
  765. FALSE,
  766. &Enabled);
  767. //
  768. // Output System-Wide information
  769. //
  770. DisplaySystemWideInformation( SystemInfoBegin,
  771. SystemInfoEnd
  772. );
  773. if( SystemInfoBegin != NULL ){
  774. free(SystemInfoBegin);
  775. SystemInfoBegin = NULL;
  776. }
  777. if( SystemInfoEnd != NULL ){
  778. free(SystemInfoEnd);
  779. SystemInfoEnd = NULL;
  780. }
  781. //
  782. // Output results
  783. //
  784. if ( bDisplayTaskSummary || (gVerbose & VERBOSE_PROFILING) ) {
  785. FPRINTF(stdout, "\n--- Process List and Summary At The End of Data Collection ---\n\n");
  786. if ( bDisplayTaskSummary ) {
  787. DisplayRunningTasksSummary (gTlistStart,
  788. tlist
  789. );
  790. } else {
  791. FPRINTF(stdout, " Pid Process\n");
  792. FPRINTF(stdout, " ------- -----------\n");
  793. for (i=0; i < NumTasks; i++) {
  794. FPRINTF(stdout, "%12I64d %32s\n",
  795. tlist[i].ProcessId,
  796. &tlist[i].ProcessName
  797. );
  798. }
  799. }
  800. }
  801. if( StartInfo2 != NULL ){
  802. free(StartInfo2);
  803. StartInfo2 = NULL;
  804. }
  805. if( StopInfo2 != NULL ){
  806. free(StopInfo2);
  807. StopInfo2 = NULL;
  808. }
  809. ProcToMonitor = gProcessList;
  810. for (i=0; i<gNumProcToMonitor; i++){
  811. if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE){
  812. FPRINTF(stdout, "\n----------------------------------------------------------------\n\n");
  813. FPRINTF(stdout, "Results for User Mode Process %s (PID = %I64d)\n",
  814. ProcToMonitor->ProcessName,
  815. ProcToMonitor->Pid
  816. );
  817. if(bIncludeGeneralInfo)
  818. OutputProcessPerfInfo ( tlist, NumTasks, ProcToMonitor);
  819. if(bIncludeUserProcLocksInfo)
  820. GetProcessLocksInformation( ProcToMonitor,
  821. RTL_QUERY_PROCESS_LOCKS,
  822. OUTPUT
  823. );
  824. FPRINTF(stdout, "------------------------------------------------------------------\n\n");
  825. }
  826. else{
  827. FPRINTF(stdout, "\n-----------------------------\n\n");
  828. FPRINTF(stdout, "Results for Kernel Mode:\n");
  829. FPRINTF(stdout, "-----------------------------\n\n");
  830. if( bIncludeGeneralInfo && bSystemThreadsInfo )
  831. OutputProcessPerfInfo( tlist, NumTasks, ProcToMonitor);
  832. }
  833. OutputResults(stdout, ProcToMonitor);
  834. //MC
  835. if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE){
  836. if( ProcToMonitor->JITHeapLocationsStart != NULL ){ //Meaning we do have JIT modules monitored in this process
  837. //So the Managed Code Helper library is already loaded
  838. pfnAttachToProcess((DWORD)ProcToMonitor->Pid);
  839. ProcToMonitor->JITHeapLocationsStop = pfnGetJitRange();
  840. pfnDetachFromProcess();
  841. OutputJITRangeComparison(ProcToMonitor);
  842. }
  843. }
  844. //MC
  845. ProcToMonitor = ProcToMonitor->Next;
  846. }
  847. //
  848. // Cleanup
  849. //
  850. if( tlist != NULL ){
  851. free(tlist);
  852. tlist = NULL;
  853. }
  854. if(gpProcDummy != NULL){
  855. free(gpProcDummy);
  856. gpProcDummy = NULL;
  857. }
  858. if(gSymbol != NULL){
  859. free(gSymbol);
  860. gSymbol = NULL;
  861. }
  862. if(gSysBasicInfo != NULL){
  863. free(gSysBasicInfo);
  864. gSysBasicInfo = NULL;
  865. }
  866. if(gulActiveSources!= NULL){
  867. free(gulActiveSources);
  868. gulActiveSources = NULL;
  869. }
  870. //MC
  871. if( ghMCLib != NULL){
  872. FreeLibrary(ghMCLib);
  873. ghMCLib = NULL;
  874. }
  875. //MC
  876. SetConsoleCtrlHandler(CtrlcH,FALSE);
  877. FPRINTF(stdout, "================================= END OF RUN ==================================\n");
  878. FPRINTF(stderr, "============================== NORMAL END OF RUN ==============================\n");
  879. //
  880. // Clean up allocated IMAGEHLP resources
  881. //
  882. ProcToMonitor = gProcessList;
  883. for (i=0; i<gNumProcToMonitor; i++){
  884. (void)SymCleanup( ProcToMonitor->ProcessHandle );
  885. ProcToMonitor = ProcToMonitor->Next;
  886. }
  887. if(ghInput != NULL)
  888. CloseHandle(ghInput);
  889. if(ghOutput != NULL)
  890. CloseHandle(ghOutput);
  891. if(ghError != NULL)
  892. CloseHandle(ghError);
  893. //
  894. // Normal program exit
  895. //
  896. return(0);
  897. } // main()
  898. PMODULE
  899. GetProcessModuleInformation(
  900. IN PPROC_TO_MONITOR ProcToMonitor
  901. )
  902. {
  903. PPROCESS_BASIC_INFORMATION BasicInfo;
  904. PLIST_ENTRY LdrHead;
  905. PPEB_LDR_DATA Ldr = NULL;
  906. PPEB_LDR_DATA LdrAddress;
  907. LDR_DATA_TABLE_ENTRY LdrEntry;
  908. PLDR_DATA_TABLE_ENTRY LdrEntryAddress;
  909. PLIST_ENTRY LdrNext;
  910. UNICODE_STRING Pathname;
  911. const ULONG PathnameBufferSize = 600*sizeof(WCHAR);
  912. PWCHAR PathnameBuffer = NULL;
  913. UNICODE_STRING fullPathName;
  914. PWCHAR fullPathNameBuffer = NULL;
  915. PEB Peb;
  916. NTSTATUS Status;
  917. BOOL Success;
  918. PMODULE NewModule;
  919. PMODULE Root = NULL;
  920. PCHAR ModuleName = NULL;
  921. PCHAR moduleFullName = NULL;
  922. ANSI_STRING AnsiString;
  923. HANDLE ProcessHandle = ProcToMonitor->ProcessHandle;
  924. //MC
  925. int i, j;
  926. BOOL bMCInitialized = FALSE;
  927. //MC
  928. //
  929. // Get Peb address.
  930. //
  931. BasicInfo = malloc(sizeof(PROCESS_BASIC_INFORMATION));
  932. if(BasicInfo == NULL){
  933. FPRINTF(stderr, "Memory Allocation failed for ProcessBasicInformation in GetProcessModuleInformation\n");
  934. exit(1);
  935. }
  936. Status = NtQueryInformationProcess(ProcessHandle,
  937. ProcessBasicInformation,
  938. BasicInfo,
  939. sizeof(PROCESS_BASIC_INFORMATION),
  940. NULL
  941. );
  942. if (!NT_SUCCESS(Status)) {
  943. FPRINTF(stderr, "NtQueryInformationProcess failed status %08lx\n", Status);
  944. ProcToMonitor->ProcessName = "???(May Be gone)";
  945. goto CLEANUP;
  946. }
  947. if (BasicInfo->PebBaseAddress == NULL) {
  948. FPRINTF(stderr, "GetProcessModuleInformation: process has no Peb.\n");
  949. ProcToMonitor->ProcessName = "???(May Be gone)";
  950. goto CLEANUP;
  951. }
  952. //
  953. // Read Peb to get Ldr.
  954. //
  955. Success = ReadProcessMemory(ProcessHandle,
  956. BasicInfo->PebBaseAddress,
  957. &Peb,
  958. sizeof(Peb),
  959. NULL);
  960. if (!Success) {
  961. FPRINTF(stderr, "ReadProcessMemory to get the PEB failed, error %d\n", GetLastError());
  962. ProcToMonitor->ProcessName = "???(May Be Gone)";
  963. goto CLEANUP;
  964. }
  965. LdrAddress = Peb.Ldr;
  966. if (LdrAddress == NULL) {
  967. FPRINTF(stderr, "Process's LdrAddress is NULL\n");
  968. ProcToMonitor->ProcessName = "???(May Be Gone)";
  969. goto CLEANUP;
  970. }
  971. //
  972. // Read Ldr to get Ldr entries.
  973. //
  974. Ldr = malloc(sizeof(PEB_LDR_DATA));
  975. if(Ldr == NULL){
  976. FPRINTF(stderr, "Memory Allocation failed for Ldr in GetProcessModuleInformation\n");
  977. exit(1);
  978. }
  979. Success = ReadProcessMemory(ProcessHandle,
  980. LdrAddress,
  981. Ldr,
  982. sizeof(PEB_LDR_DATA),
  983. NULL);
  984. if (!Success) {
  985. FPRINTF(stderr, "ReadProcessMemory to get Ldr entries failed, errror %d\n", GetLastError());
  986. ProcToMonitor->ProcessName = "???(May Be Gone)";
  987. goto CLEANUP;
  988. }
  989. //
  990. // Read Ldr table entries to get image information.
  991. //
  992. if (Ldr->InLoadOrderModuleList.Flink == NULL) {
  993. FPRINTF(stderr, "Ldr.InLoadOrderModuleList == NULL\n");
  994. ProcToMonitor->ProcessName = "???(May Be Gone)";
  995. goto CLEANUP;
  996. }
  997. LdrHead = &LdrAddress->InLoadOrderModuleList;
  998. Success = ReadProcessMemory(ProcessHandle,
  999. &LdrHead->Flink,
  1000. &LdrNext,
  1001. sizeof(LdrNext),
  1002. NULL);
  1003. if (!Success) {
  1004. FPRINTF(stderr, "ReadProcessMemory to get Ldr head failed, errror %d\n", GetLastError());
  1005. ProcToMonitor->ProcessName = "???(May Be Gone)";
  1006. goto CLEANUP;
  1007. }
  1008. //
  1009. // Loop through InLoadOrderModuleList.
  1010. //
  1011. PathnameBuffer = (PWCHAR)malloc(PathnameBufferSize);
  1012. if(PathnameBuffer == NULL){
  1013. FPRINTF(stderr, "Memory Allocation failed for PathNameBuffer in GetProcessModuleInformation\n");
  1014. exit(1);
  1015. }
  1016. fullPathNameBuffer = (PWCHAR)malloc( _MAX_PATH*sizeof(WCHAR));
  1017. if(fullPathNameBuffer == NULL){
  1018. FPRINTF(stderr, "Memory Allocation failed for FullPathNameBuffer in GetProcessModuleInformation\n");
  1019. exit(1);
  1020. }
  1021. ModuleName = malloc(cMODULE_NAME_STRLEN*sizeof(CHAR));
  1022. if(ModuleName == NULL){
  1023. FPRINTF(stderr, "Memory Allocation failed for ModuleName in GetProcessModuleInformation\n");
  1024. exit(1);
  1025. }
  1026. moduleFullName = malloc(_MAX_PATH*sizeof(CHAR));
  1027. if(moduleFullName == NULL){
  1028. FPRINTF(stderr, "Memory Allocation failed for ModuleFullName in GetProcessModuleInformation\n");
  1029. exit(1);
  1030. }
  1031. while (LdrNext != LdrHead) {
  1032. LdrEntryAddress = CONTAINING_RECORD(LdrNext,
  1033. LDR_DATA_TABLE_ENTRY,
  1034. InLoadOrderLinks);
  1035. Success = ReadProcessMemory(ProcessHandle,
  1036. LdrEntryAddress,
  1037. &LdrEntry,
  1038. sizeof(LdrEntry),
  1039. NULL);
  1040. if (!Success) {
  1041. FPRINTF(stderr, "ReadProcessMemory to get LdrEntry failed, errror %d\n", GetLastError());
  1042. ProcToMonitor->ProcessName = "???(May Be Gone)";
  1043. goto CLEANUP;
  1044. }
  1045. //
  1046. // Get copy of image name.
  1047. //
  1048. Pathname = LdrEntry.BaseDllName;
  1049. if( Pathname.MaximumLength > PathnameBufferSize ){
  1050. free( PathnameBuffer ); //We already know it's not NULL
  1051. PathnameBuffer = (PWCHAR)malloc( Pathname.MaximumLength );
  1052. if(PathnameBuffer == NULL){
  1053. FPRINTF(stderr, "Memory Allocation failed for PathNameBuffer(2) in GetProcessModuleInformation\n");
  1054. exit(1);
  1055. }
  1056. }
  1057. Pathname.Buffer = &PathnameBuffer[0];
  1058. Success = ReadProcessMemory(ProcessHandle,
  1059. LdrEntry.BaseDllName.Buffer,
  1060. Pathname.Buffer,
  1061. Pathname.MaximumLength,
  1062. NULL);
  1063. if (!Success) {
  1064. FPRINTF(stderr, "ReadProcessMemory to get image name failed, errror %d\n", GetLastError());
  1065. ProcToMonitor->ProcessName = "???(May Be Gone)";
  1066. goto CLEANUP;
  1067. }
  1068. //
  1069. // Get Copy of image full pathname
  1070. //
  1071. fullPathName = LdrEntry.FullDllName;
  1072. if( fullPathName.MaximumLength > _MAX_PATH*sizeof(WCHAR) ){
  1073. free( fullPathNameBuffer ); //We already know it's not NULL
  1074. fullPathNameBuffer = (PWCHAR)malloc( fullPathName.MaximumLength );
  1075. if(fullPathNameBuffer == NULL){
  1076. FPRINTF(stderr, "Memory Allocation failed for FullPathNameBuffer(2) in GetProcessModuleInformation\n");
  1077. exit(1);
  1078. }
  1079. }
  1080. fullPathName.Buffer = fullPathNameBuffer;
  1081. Success = ReadProcessMemory( ProcessHandle,
  1082. LdrEntry.FullDllName.Buffer,
  1083. fullPathName.Buffer,
  1084. fullPathName.MaximumLength,
  1085. NULL
  1086. );
  1087. if (!Success) {
  1088. FPRINTF(stderr, "ReadProcessMemory to get image full path name failed, errror %d\n", GetLastError());
  1089. ProcToMonitor->ProcessName = "???(May Be Gone)";
  1090. goto CLEANUP;
  1091. }
  1092. //
  1093. // Create module
  1094. //
  1095. AnsiString.Buffer = ModuleName;
  1096. AnsiString.MaximumLength = cMODULE_NAME_STRLEN*sizeof(CHAR);
  1097. AnsiString.Length = 0;
  1098. Status = RtlUnicodeStringToAnsiString(&AnsiString, &Pathname, cDONOT_ALLOCATE_DESTINATION_STRING);
  1099. if (!NT_SUCCESS(Status)) {
  1100. FPRINTF(stderr, "KERNRATE WARNING:\n");
  1101. FPRINTF(stderr, "RtlUnicodeStringToAnsiString failed in GetProcessModuleInformation, status= %08lx\n", Status);
  1102. if(Status == STATUS_BUFFER_OVERFLOW){
  1103. FPRINTF(stderr, "Source String: %S\nLength= %ld", &Pathname.Buffer, Pathname.Length);
  1104. FPRINTF(stderr, "Maximum destination string Length allowed is %d\n", cMODULE_NAME_STRLEN);
  1105. }
  1106. }
  1107. ModuleName[AnsiString.Length] = '\0';
  1108. AnsiString.Buffer = moduleFullName;
  1109. AnsiString.MaximumLength = _MAX_PATH*sizeof(CHAR);
  1110. AnsiString.Length = 0;
  1111. Status = RtlUnicodeStringToAnsiString(&AnsiString, &fullPathName, cDONOT_ALLOCATE_DESTINATION_STRING );
  1112. if (!NT_SUCCESS(Status)) {
  1113. FPRINTF(stderr, "KERNRATE WARNING:\n");
  1114. FPRINTF(stderr, "RtlUnicodeStringToAnsiString failed in GetProcessModuleInformation, status= %08lx\n", Status);
  1115. if(Status == STATUS_BUFFER_OVERFLOW){
  1116. FPRINTF(stderr, "Source String: %S\nLength= %ld", &fullPathName.Buffer, fullPathName.Length);
  1117. FPRINTF(stderr, "Maximum destination string Length allowed is %d\n", _MAX_PATH);
  1118. }
  1119. }
  1120. moduleFullName[AnsiString.Length] = '\0';
  1121. NewModule = CreateNewModule(ProcToMonitor,
  1122. ModuleName,
  1123. moduleFullName,
  1124. (ULONG64)LdrEntry.DllBase,
  1125. LdrEntry.SizeOfImage);
  1126. if( NewModule != NULL){
  1127. ProcToMonitor->ModuleCount += 1;
  1128. NewModule->Next = Root;
  1129. Root = NewModule;
  1130. LdrNext = LdrEntry.InLoadOrderLinks.Flink;
  1131. }else{
  1132. FPRINTF(stderr, "KERNRATE: Failed to create new module for %s\n", ModuleName);
  1133. }
  1134. //
  1135. //The first module in the LDR InLoadOrder module list is the Process
  1136. //
  1137. if(ProcToMonitor->ModuleCount == 1){
  1138. PCHAR Name = calloc(1, cMODULE_NAME_STRLEN*sizeof(CHAR));
  1139. if(Name != NULL){
  1140. strncpy(Name, ModuleName, cMODULE_NAME_STRLEN-1);
  1141. Name[ cMODULE_NAME_STRLEN-1 ] = '\0';
  1142. ProcToMonitor->ProcessName = _strupr(Name);
  1143. }
  1144. }
  1145. //MC
  1146. //
  1147. // Initialize Managed Code Support if Managed Code main library is present
  1148. //
  1149. if( !_stricmp(ModuleName, MANAGED_CODE_MAINLIB) ){
  1150. bMCInitialized = InitializeManagedCodeSupport( ProcToMonitor );
  1151. if( !bMCInitialized ){
  1152. FPRINTF(stderr, "\nKERNRATE: Failed to Initialize Support for Managed Code for Pid = %I64d\n", ProcToMonitor->Pid);
  1153. FPRINTF(stderr, "Use Verbose Level 4 for More Details\n");
  1154. }
  1155. }
  1156. //MC
  1157. }// while (LdrNext != LdrHead)
  1158. //MC
  1159. //
  1160. // If Managed Code helper lib is loaded and we do have JIT ranges present, let's create a module for each
  1161. //
  1162. if( bMCInitialized && bMCJitRangesExist ){
  1163. i = 0;
  1164. j = 0;
  1165. while( ProcToMonitor->JITHeapLocationsStart[i] != 0 ){
  1166. _snprintf( ModuleName, cMODULE_NAME_STRLEN*sizeof(CHAR)-1, "JIT%d", j );
  1167. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Creating JIT module %s\n", ModuleName));
  1168. strncpy(moduleFullName, "JIT_TYPE", cMODULE_NAME_STRLEN-1);
  1169. NewModule = CreateNewModule(ProcToMonitor,
  1170. ModuleName,
  1171. moduleFullName, //"JIT_TYPE",
  1172. (ULONG64)ProcToMonitor->JITHeapLocationsStart[i] ,
  1173. (ULONG)ProcToMonitor->JITHeapLocationsStart[i+1]
  1174. );
  1175. if(NewModule != NULL){
  1176. ProcToMonitor->ModuleCount += 1;
  1177. NewModule->Next = Root;
  1178. Root = NewModule;
  1179. }else{
  1180. FPRINTF(stderr, "KERNRATE: Failed to create new JIT module for %s\n", ModuleName);
  1181. }
  1182. i += 2;
  1183. j += 1;
  1184. }
  1185. }
  1186. //MC
  1187. //
  1188. // Cleanup
  1189. //
  1190. CLEANUP:
  1191. if(BasicInfo != NULL){
  1192. free(BasicInfo);
  1193. BasicInfo = NULL;
  1194. }
  1195. if(Ldr != NULL){
  1196. free(Ldr);
  1197. Ldr = NULL;
  1198. }
  1199. if(PathnameBuffer != NULL){
  1200. free(PathnameBuffer);
  1201. PathnameBuffer = NULL;
  1202. }
  1203. if(fullPathNameBuffer != NULL){
  1204. free(fullPathNameBuffer);
  1205. fullPathNameBuffer = NULL;
  1206. }
  1207. if(ModuleName != NULL){
  1208. free(ModuleName);
  1209. ModuleName = NULL;
  1210. }
  1211. if(moduleFullName != NULL){
  1212. free(moduleFullName);
  1213. moduleFullName = NULL;
  1214. }
  1215. return(Root);
  1216. } // GetProcessModuleInformation()
  1217. PMODULE
  1218. GetKernelModuleInformation(
  1219. VOID
  1220. )
  1221. {
  1222. PRTL_PROCESS_MODULES modules;
  1223. PUCHAR buffer;
  1224. ULONG bufferSize = 1*1024*1024; //not a constant!
  1225. ULONG i;
  1226. PMODULE root = NULL;
  1227. PMODULE newModule;
  1228. NTSTATUS status;
  1229. do {
  1230. buffer = malloc(bufferSize);
  1231. if (buffer == NULL) {
  1232. FPRINTF(stderr, "Buffer Allocation failed for ModuleInformation in GetKernelModuleInformation\n");
  1233. exit(1);
  1234. }
  1235. status = NtQuerySystemInformation(SystemModuleInformation,
  1236. buffer,
  1237. bufferSize,
  1238. &bufferSize);
  1239. if (NT_SUCCESS(status)) {
  1240. break;
  1241. }
  1242. if (status == STATUS_INFO_LENGTH_MISMATCH) {
  1243. free(buffer);
  1244. buffer = NULL;
  1245. } else {
  1246. FPRINTF(stderr, "GetKernelModuleInformation failed call to get SystemModuleInformation - ");
  1247. if(status == STATUS_WORKING_SET_QUOTA)
  1248. FPRINTF(stderr, "Insufficient process working set\n");
  1249. if(status == STATUS_INSUFFICIENT_RESOURCES)
  1250. FPRINTF(stderr, "Insufficient system resources\n");
  1251. exit(1);
  1252. }
  1253. } while (buffer == NULL);
  1254. #ifdef _WIN64
  1255. #define VerboseModuleFormat "start end "
  1256. #else // !_WIN64
  1257. #define VerboseModuleFormat "start end "
  1258. #endif // !_WIN64
  1259. VerbosePrint(( VERBOSE_MODULES, "Kernel Modules ========== System HighestUserAddress = 0x%p\n"
  1260. VerboseModuleFormat
  1261. "module name [full name]\n",
  1262. (PVOID)gSysBasicInfo->MaximumUserModeAddress
  1263. ));
  1264. #undef VerboseModuleFormat
  1265. modules = (PRTL_PROCESS_MODULES)buffer;
  1266. gKernelModuleCount = modules->NumberOfModules;
  1267. for (i=0; i < gKernelModuleCount; i++) {
  1268. PRTL_PROCESS_MODULE_INFORMATION Module;
  1269. Module = &modules->Modules[i];
  1270. if ((ULONG_PTR)Module->ImageBase > gSysBasicInfo->MaximumUserModeAddress) {
  1271. newModule = CreateNewModule(gpSysProc,
  1272. (PCHAR)(Module->FullPathName+Module->OffsetToFileName),
  1273. (PCHAR)Module->FullPathName,
  1274. (ULONG64)(ULONG_PTR)Module->ImageBase,
  1275. Module->ImageSize);
  1276. assert( newModule );
  1277. newModule->Next = root;
  1278. root = newModule;
  1279. }
  1280. else {
  1281. #define VerboseModuleFormat "0x%p 0x%p "
  1282. VerbosePrint(( VERBOSE_MODULES, VerboseModuleFormat " %s [%s] - Base > HighestUserAddress\n",
  1283. (PVOID)Module->ImageBase,
  1284. (PVOID)((ULONG64)Module->ImageBase + (ULONG64)Module->ImageSize),
  1285. Module->FullPathName+Module->OffsetToFileName,
  1286. Module->FullPathName
  1287. ));
  1288. #undef VerboseModuleFormat
  1289. }
  1290. }
  1291. //
  1292. // Cleanup
  1293. //
  1294. if(buffer != NULL){
  1295. free(buffer);
  1296. buffer = NULL;
  1297. }
  1298. return(root);
  1299. } // GetKernelModuleInformation()
  1300. VOID
  1301. CreateProfiles(
  1302. IN PMODULE Root,
  1303. IN PPROC_TO_MONITOR ProcToMonitor
  1304. )
  1305. {
  1306. PMODULE Current;
  1307. KPROFILE_SOURCE ProfileSource;
  1308. NTSTATUS Status;
  1309. PRATE_DATA Rate;
  1310. ULONG ProfileSourceIndex, Index;
  1311. ULONG BucketsNeeded;
  1312. HANDLE hProc = NULL;
  1313. KAFFINITY AffinityMask = (KAFFINITY)-1;
  1314. LONG CpuNumber;
  1315. //
  1316. // To get the kernel profile NtCreateProfile has to be called with hProc = NULL
  1317. //
  1318. hProc = (ProcToMonitor->ProcessHandle == SYM_KERNEL_HANDLE)? NULL : ProcToMonitor->ProcessHandle;
  1319. for (Index=0; Index < gTotalActiveSources; Index++) {
  1320. ProfileSourceIndex = gulActiveSources[Index];
  1321. ProfileSource = ProcToMonitor->Source[ProfileSourceIndex].ProfileSource;
  1322. Current = Root;
  1323. while (Current != NULL) {
  1324. BucketsNeeded = BUCKETS_NEEDED(Current->Length);
  1325. Rate = &Current->Rate[ProfileSourceIndex];
  1326. Rate->TotalCount = calloc(gProfileProcessors, sizeof(ULONGLONG) );
  1327. if(Rate->TotalCount == NULL){
  1328. FPRINTF(stderr, "KERNRATE: Memory allocation failed for TotalCount in CreateProfiles\n");
  1329. exit(1);
  1330. }
  1331. Rate->CurrentCount = calloc(gProfileProcessors, sizeof(ULONG) );
  1332. if(Rate->CurrentCount == NULL){
  1333. FPRINTF(stderr, "KERNRATE: Memory allocation failed for CurrentCount in CreateProfiles\n");
  1334. exit(1);
  1335. }
  1336. Rate->ProfileHandle = calloc(gProfileProcessors, sizeof(HANDLE) );
  1337. if(Rate->ProfileHandle == NULL){
  1338. FPRINTF(stderr, "KERNRATE: Memory allocation failed for ProfileHandle in CreateProfiles\n");
  1339. exit(1);
  1340. }
  1341. Rate->ProfileBuffer = calloc(gProfileProcessors, sizeof(PULONG) );
  1342. if(Rate->ProfileBuffer == NULL){
  1343. FPRINTF(stderr, "KERNRATE: Memory allocation failed for ProfileBuffer in CreateProfiles\n");
  1344. exit(1);
  1345. }
  1346. Rate->StartTime = 0;
  1347. Rate->TotalTime = 0;
  1348. Rate->Rate = 0;
  1349. Rate->GrandTotalCount = 0;
  1350. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  1351. if ( bProfileByProcessor ){
  1352. AffinityMask = (1 << CpuNumber);
  1353. }
  1354. Rate->TotalCount[CpuNumber] = 0;
  1355. Rate->CurrentCount[CpuNumber] = 0;
  1356. if (Current->bZoom) {
  1357. Rate->ProfileBuffer[CpuNumber] = calloc(1, BucketsNeeded*sizeof(ULONG));
  1358. if (Rate->ProfileBuffer[CpuNumber] == NULL) {
  1359. FPRINTF(stderr,
  1360. "KERNRATE: Memory allocation failed for Zoom buffer, Module: %s\n",
  1361. Current->module_Name
  1362. );
  1363. exit(1);
  1364. }
  1365. if ( !gAffinityMask || (AffinityMask & gAffinityMask) ) {
  1366. Status = NtCreateProfile(&Rate->ProfileHandle[CpuNumber],
  1367. hProc,
  1368. (PVOID64)Current->Base,
  1369. Current->Length,
  1370. gLog2ZoomBucket,
  1371. Rate->ProfileBuffer[CpuNumber],
  1372. sizeof(ULONG)*BucketsNeeded,
  1373. ProfileSource,
  1374. AffinityMask
  1375. );
  1376. if (!NT_SUCCESS(Status)) {
  1377. FPRINTF(stderr,
  1378. "NtCreateProfile on zoomed module %s, source %d failed %08lx\n",
  1379. Current->module_Name,
  1380. ProfileSource,
  1381. Status
  1382. );
  1383. FPRINTF(stderr,
  1384. "Base %p\nLength %08lx\nBufferLength %08lx\n",
  1385. (PVOID64)Current->Base,
  1386. Current->Length,
  1387. BucketsNeeded
  1388. );
  1389. exit(1);
  1390. }
  1391. else if ( gVerbose & VERBOSE_PROFILING ) {
  1392. FPRINTF(stderr,
  1393. "Created zoomed profiling on module %s with source: %s\n",
  1394. Current->module_Name,
  1395. ProcToMonitor->Source[ProfileSourceIndex].ShortName
  1396. );
  1397. }
  1398. }
  1399. } else {
  1400. Status = NtCreateProfile(&Rate->ProfileHandle[CpuNumber],
  1401. hProc,
  1402. (PVOID64)Current->Base,
  1403. Current->Length,
  1404. 31,
  1405. &Rate->CurrentCount[CpuNumber],
  1406. sizeof(Rate->CurrentCount[CpuNumber]),
  1407. ProfileSource,
  1408. AffinityMask
  1409. );
  1410. if (!NT_SUCCESS(Status)) {
  1411. FPRINTF(stderr,
  1412. "NtCreateProfile on module %s, source %d failed %08lx\n",
  1413. Current->module_Name,
  1414. ProfileSource,
  1415. Status
  1416. );
  1417. exit(1);
  1418. }
  1419. else if ( gVerbose & VERBOSE_PROFILING ) {
  1420. FPRINTF(stderr,
  1421. "Created profiling on module %s with source: %s\n",
  1422. Current->module_Name,
  1423. ProcToMonitor->Source[ProfileSourceIndex].ShortName
  1424. );
  1425. }
  1426. }
  1427. } // CpuNumber
  1428. Current = Current->Next;
  1429. } // Module list
  1430. } // ProfileSourceIndex
  1431. }
  1432. static void
  1433. SetModuleName( PMODULE Module, PCHAR szName )
  1434. {
  1435. assert ( Module );
  1436. assert ( szName );
  1437. (void)strncpy( Module->module_Name, szName, sizeof(Module->module_Name) - 1 );
  1438. Module->module_Name[lstrlen(Module->module_Name)] = '\0';
  1439. return;
  1440. } // SetModuleName()
  1441. VOID
  1442. GetConfiguration(
  1443. int argc,
  1444. char *argv[]
  1445. )
  1446. /*++
  1447. Routine Description:
  1448. Gets configuration for this run.
  1449. Arguments:
  1450. None
  1451. Return Value:
  1452. None, exits on failure.
  1453. --*/
  1454. {
  1455. DWORD NumTasks, m;
  1456. CHAR tmpName[USER_SYMPATH_LENGTH];
  1457. NTSTATUS Status;
  1458. PPROC_TO_MONITOR ProcToMonitor = NULL;
  1459. PMODULE ZoomModule;
  1460. PMODULE tmpModule;
  1461. LONGLONG Pid;
  1462. int i, j, k;
  1463. int tJump = 0;
  1464. HANDLE SymHandle;
  1465. ULONG MaxProcSameName = MAX_PROC_SAME_NAME;
  1466. ULONG ProfileSourceIndex;
  1467. ULONG IDataCommonRate;
  1468. ULONG SourcesSoFar = 1; //Source TIME is on by default
  1469. ULONG ulVerbose;
  1470. BOOL bZoomSpecified = FALSE;
  1471. BOOL bTlistInitialized = FALSE;
  1472. BOOL tlistVerbose = FALSE;
  1473. BOOL tlistDisplayed = FALSE;
  1474. INPUT_ERROR_TYPE ietResult;
  1475. //
  1476. // Assume system wide profile.
  1477. //
  1478. gProfileProcessors = 1;
  1479. //
  1480. // The following preliminary-check purpose is to get rid of most command line precedence rules
  1481. //
  1482. for (i=1; i < argc; i++) {
  1483. if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
  1484. switch ( toupper(argv[i][1]) ) {
  1485. case 'E':
  1486. //
  1487. // User asked to exclude system-wide and process specific general information
  1488. // (context switches, memory usage, etc.)
  1489. //
  1490. ietResult = IsInputValid(argc,
  1491. i,
  1492. argv,
  1493. "",
  1494. NULL,
  1495. NULL,
  1496. 0,
  1497. ORDER_ANY,
  1498. OPTIONAL_ANY
  1499. );
  1500. if(ietResult == INPUT_GOOD){
  1501. bIncludeGeneralInfo = FALSE;
  1502. } else if (ietResult == BOGUS_ENTRY){
  1503. ExitWithUnknownOptionMessage(argv[i+1]);
  1504. } else {
  1505. ExitWithUnknownOptionMessage(argv[i]);
  1506. }
  1507. break;
  1508. case 'M':
  1509. //
  1510. // Do multi-processor profile. Allow optional processor affinity mask to be able to
  1511. // profile only selected processors and reduce profiling overhead on 64 and 32 way.
  1512. //
  1513. // The following precedence rule check allows to save some memory footprint
  1514. // by allocating the zoom module based on the actual gProfileProcessors instead
  1515. // of the total NumberOfProcessors
  1516. //
  1517. ietResult = IsInputValid(argc,
  1518. i,
  1519. argv,
  1520. "#",
  1521. NULL,
  1522. tmpName, //Just used as a temp storage
  1523. USER_SYMPATH_LENGTH, //with long enough space
  1524. ORDER_ANY,
  1525. OPTIONAL_ANY
  1526. );
  1527. if(ietResult == MISSING_REQUIRED_NUMBER){ //Allow a # although the optional mask is initially treated as a string
  1528. if(i+1 < argc && argv[i+1][0] == '0' && argv[i+1][1] == 'x' || argv[i+1][1] == 'X'){
  1529. ietResult = INPUT_GOOD;
  1530. } else {
  1531. FPRINTF(stderr, "KERNRATE: '-m# 0xN' option requires a valid (0x prefixed) hex number\n");
  1532. ExitWithUnknownOptionMessage(argv[i+1]);
  1533. }
  1534. }
  1535. if(ietResult == INPUT_GOOD){
  1536. ULONG LowMask, HighMask;
  1537. int nChars, iStart=0, iProc;
  1538. PCHAR cStart = NULL;
  1539. CHAR tmpstr[8] = "";
  1540. bProfileByProcessor = TRUE;
  1541. gProfileProcessors = gSysBasicInfo->NumberOfProcessors;
  1542. nChars = lstrlen(tmpName);
  1543. cStart = tmpName;
  1544. if( 'x' == tmpName[1] || 'X' == tmpName[1] ){
  1545. cStart = &tmpName[2];
  1546. nChars -= 2;
  1547. } else if ( 'x' == tmpName[0] || 'X' == tmpName[0] ){
  1548. cStart = &tmpName[1];
  1549. nChars -= 1;
  1550. }
  1551. if( nChars > 16 || (gProfileProcessors <= 32 && nChars > 8) ){
  1552. InvalidEntryMessage(argv[i],
  1553. argv[i+1],
  1554. "Expecting a valid HEX value, maximum 8 characters for up to 32 processors\nor 16 characters for up to 64 processors",
  1555. FALSE,
  1556. TRUE
  1557. );
  1558. }
  1559. if( nChars > 8 ){
  1560. strncpy(tmpstr, cStart, nChars-8);
  1561. HighMask = strtoul(tmpstr, NULL, 16);
  1562. iStart = nChars-8+1;
  1563. }
  1564. strncpy(tmpstr, &cStart[iStart], (nChars<=8)? nChars:8 );
  1565. LowMask = strtoul(tmpstr, NULL, 16);
  1566. gAffinityMask = (nChars<=8)? (KAFFINITY)LowMask:( (((KAFFINITY)HighMask) << 32) +(KAFFINITY)LowMask ) ;
  1567. if( gAffinityMask == 0 ){
  1568. InvalidEntryMessage(argv[i],
  1569. argv[i+1],
  1570. "Expecting a valid HEX value, maximum 8 characters for up to 32 processors\nor 16 characters for up to 64 processors",
  1571. FALSE,
  1572. TRUE
  1573. );
  1574. }
  1575. FPRINTF(stdout, "\nUser defined CPU affinity mask for profiling= 0x%p\n", (PVOID)gAffinityMask);
  1576. FPRINTF(stdout, "This will profile the following processors:\n");
  1577. for( iProc=0; iProc<gProfileProcessors; iProc++){
  1578. if((1 << iProc) & gAffinityMask)
  1579. FPRINTF(stdout, "P%d, ", iProc);
  1580. }
  1581. FPRINTF(stdout,"\n");
  1582. ++i;
  1583. } else if (ietResult == MISSING_STRING){
  1584. bProfileByProcessor = TRUE;
  1585. gProfileProcessors = gSysBasicInfo->NumberOfProcessors;
  1586. } else if (ietResult == BOGUS_ENTRY){
  1587. ExitWithUnknownOptionMessage(argv[i+1]);
  1588. } else {
  1589. ExitWithUnknownOptionMessage(argv[i]);
  1590. }
  1591. break;
  1592. default:
  1593. break;
  1594. }
  1595. } else { //if ((argv[i][0] == '-') || (argv[i][0] == '/'))
  1596. if( !strchr(argv[i], '{') ){
  1597. continue;
  1598. } else { //Exclude any command options in the curly brackets
  1599. while( i < argc && !strchr(argv[i], '}') ){
  1600. ++i;
  1601. }
  1602. }
  1603. }
  1604. }
  1605. for (i=1; i < argc; i++) {
  1606. if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
  1607. switch ( toupper(argv[i][1]) ) {
  1608. case 'T':
  1609. ietResult = IsInputValid(argc,
  1610. i,
  1611. argv,
  1612. "#",
  1613. &gMaxTasks,
  1614. NULL,
  1615. 0,
  1616. ORDER_ANY,
  1617. OPTIONAL_ANY
  1618. );
  1619. if(ietResult == INPUT_GOOD){
  1620. //
  1621. // User also wants to change the maximum number of tasks in the task list from Kernrate's default number
  1622. //
  1623. if (gMaxTasks == 0) {
  1624. InvalidEntryMessage(argv[i],
  1625. argv[i+1],
  1626. "Expecting a decimal number >0",
  1627. FALSE,
  1628. TRUE
  1629. );
  1630. }
  1631. FPRINTF(stdout, "---> Kernrate task list set to accommodate %ld processes\n", gMaxTasks);
  1632. tJump = 1;;
  1633. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
  1634. //
  1635. //User just wants task summary without changing the default maximum number of tasks
  1636. //
  1637. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  1638. ExitWithMissingEntryMessage(argv[i],
  1639. "'-t# N' option requires the maximum (decimal) number of processes in Kernrate's task list",
  1640. FALSE
  1641. );
  1642. } else if(ietResult == INVALID_NUMBER) {
  1643. InvalidEntryMessage(argv[i],
  1644. argv[i+1],
  1645. "Expecting a decimal value >0 in [ms], 0 < N < 10^9",
  1646. FALSE,
  1647. TRUE
  1648. );
  1649. } else if(ietResult == UNKNOWN_OPTION) {
  1650. ExitWithUnknownOptionMessage(argv[i]);
  1651. }
  1652. bDisplayTaskSummary = TRUE;
  1653. default:
  1654. break;
  1655. }
  1656. } else { //if ((argv[i][0] == '-') || (argv[i][0] == '/'))
  1657. if( !strchr(argv[i], '{') ){
  1658. continue;
  1659. } else { //Exclude any command options in the curly brackets
  1660. while( i < argc && !strchr(argv[i], '}') ){
  1661. ++i;
  1662. }
  1663. }
  1664. }
  1665. }
  1666. for (i=1; i < argc; i++) {
  1667. if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
  1668. BOOL bIncludeProcessThreadsInfo = FALSE;
  1669. switch ( toupper(argv[i][1]) ) {
  1670. case 'A':
  1671. //
  1672. // Do both Kernel and User mode profiling
  1673. //
  1674. ietResult = IsInputValid(argc,
  1675. i,
  1676. argv,
  1677. "v",
  1678. NULL,
  1679. NULL,
  1680. 0,
  1681. ORDER_ANY,
  1682. OPTIONAL_ANY);
  1683. if(ietResult == INPUT_GOOD){
  1684. bCombinedProfile = TRUE;
  1685. FPRINTF(stdout, "\n---> Profiling both Kernel and User Modes\n");
  1686. //
  1687. // Check if extra options are present
  1688. //
  1689. if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ){
  1690. //
  1691. // User wants system threads information
  1692. //
  1693. tlistVerbose = TRUE;
  1694. bSystemThreadsInfo = TRUE;
  1695. }
  1696. } else if (ietResult == BOGUS_ENTRY){
  1697. ExitWithUnknownOptionMessage(argv[i+1]);
  1698. } else {
  1699. ExitWithUnknownOptionMessage(argv[i]);
  1700. }
  1701. if( bIncludeGeneralInfo || tlistVerbose) {
  1702. if ( (!bTlistInitialized && tlistVerbose) || (bSystemThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE) ) {
  1703. //
  1704. // If we already took a tlist but that's the first time thread info is required,
  1705. // we'll have to refresh it and take thread info as well
  1706. //
  1707. if( bSystemThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE ){
  1708. bIncludeThreadsInfo = TRUE;
  1709. }
  1710. //
  1711. // get the task list for the system (this is needed to identify the System Process ID)
  1712. //
  1713. if ( !bTlistInitialized ) {
  1714. gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
  1715. if (gTlistStart == NULL) {
  1716. FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(1)\n");
  1717. exit(1);
  1718. }
  1719. }
  1720. NumTasks = GetTaskList( gTlistStart, gMaxTasks );
  1721. bTlistInitialized = TRUE;
  1722. gNumTasksStart = NumTasks;
  1723. }
  1724. }
  1725. if( bTlistInitialized && tlistVerbose ){
  1726. if( tlistDisplayed == FALSE ){
  1727. FPRINTF(stdout, "\nRunning processes found before profile start:\n");
  1728. FPRINTF(stdout, " Pid Process\n");
  1729. FPRINTF(stdout, " ------- -----------\n");
  1730. for (m=0, k=0; m < NumTasks; m++) {
  1731. FPRINTF(stdout, "%12I64d %32s\n",
  1732. gTlistStart[m].ProcessId,
  1733. gTlistStart[m].ProcessName
  1734. );
  1735. }
  1736. if( tlistDisplayed == FALSE ){
  1737. FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
  1738. //
  1739. //Lets not get carried away if the user specified the verbose option more than once...
  1740. //
  1741. tlistDisplayed = TRUE;
  1742. tlistVerbose = FALSE;
  1743. }
  1744. }
  1745. }
  1746. break;
  1747. case 'B':
  1748. //
  1749. // Set Zoom Bucket Size
  1750. //
  1751. ietResult = IsInputValid(argc,
  1752. i,
  1753. argv,
  1754. "#",
  1755. &gZoomBucket,
  1756. NULL,
  1757. 0,
  1758. ORDER_ANY,
  1759. OPTIONAL_NONE
  1760. );
  1761. if(ietResult == INPUT_GOOD){
  1762. if (gZoomBucket == 0) {
  1763. InvalidEntryMessage(argv[i],
  1764. argv[i+1],
  1765. "Invalid bucket size, expecting a decimal value\nBucket size must be power of 2, minimum bucket size is 4",
  1766. FALSE,
  1767. TRUE
  1768. );
  1769. }
  1770. for (gLog2ZoomBucket=1; (1UL<<gLog2ZoomBucket) < gZoomBucket; gLog2ZoomBucket++)
  1771. // Empty Loop
  1772. ;
  1773. if ( ( gZoomBucket < MINIMUM_ZOOM_BUCKET_SIZE ) || ( gZoomBucket != (1UL<<gLog2ZoomBucket) ) ) {
  1774. InvalidEntryMessage(argv[i],
  1775. argv[i+1],
  1776. "Bucket size must be power of 2, minimum bucket size is 4",
  1777. FALSE,
  1778. TRUE
  1779. );
  1780. }
  1781. ++i;
  1782. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) {
  1783. ExitWithMissingEntryMessage(argv[i],
  1784. "'-b N' option requires bucket size (minimum 4 bytes), space separated",
  1785. FALSE
  1786. );
  1787. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  1788. ExitWithMissingEntryMessage(argv[i],
  1789. "'-b# N' option requires bucket size (minimum 4 bytes), space separated",
  1790. FALSE
  1791. );
  1792. } else if(ietResult == INVALID_NUMBER) {
  1793. InvalidEntryMessage(argv[i],
  1794. argv[i+1],
  1795. "Invalid bucket size, expecting a decimal value\nBucket size must be power of 2, minimum bucket size is 4",
  1796. FALSE,
  1797. TRUE
  1798. );
  1799. } else if(ietResult == UNKNOWN_OPTION) {
  1800. ExitWithUnknownOptionMessage(argv[i]);
  1801. }
  1802. FPRINTF(stdout, "---> Profile Bucket Size Set to %u bytes\n", gZoomBucket);
  1803. break;
  1804. case 'C':
  1805. //
  1806. //Use old sampling scheme (sample one source at a time, switch between sources (and monitored processes)
  1807. //every gChangeInterval in ms). If not specified, all sources will be turned on simultaneously.
  1808. //
  1809. ietResult = IsInputValid(argc,
  1810. i,
  1811. argv,
  1812. "#",
  1813. &gChangeInterval,
  1814. NULL,
  1815. 0,
  1816. ORDER_ANY,
  1817. OPTIONAL_ANY
  1818. );
  1819. if(ietResult == INPUT_GOOD){
  1820. //
  1821. // User wants to sample cyclically one source at a time and also to specify the interval
  1822. //
  1823. if (gChangeInterval == 0) {
  1824. InvalidEntryMessage(argv[i],
  1825. argv[i+1],
  1826. "Expecting a decimal value >0 in [ms], 0 < N < 10^9",
  1827. FALSE,
  1828. TRUE
  1829. );
  1830. }
  1831. ++i;
  1832. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
  1833. //
  1834. // User wants to sample cyclically one source at a time using the default interval
  1835. //
  1836. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  1837. ExitWithMissingEntryMessage(argv[i],
  1838. "'-c# N' option requires a decimal value in [ms], 0 < N < 10^9",
  1839. FALSE
  1840. );
  1841. } else if(ietResult == INVALID_NUMBER) {
  1842. InvalidEntryMessage(argv[i],
  1843. argv[i+1],
  1844. "Expecting a decimal value >0 in [ms], 0 < N < 10^9",
  1845. FALSE,
  1846. TRUE
  1847. );
  1848. } else if(ietResult == UNKNOWN_OPTION) {
  1849. ExitWithUnknownOptionMessage(argv[i]);
  1850. }
  1851. bOldSampling = TRUE;
  1852. FPRINTF(stdout, "---> Using Cyclic Sampling Scheme (Profiling One Source At A time)\n");
  1853. FPRINTF(stdout, "Change Interval between Profile Sources Set to %u[ms]\n", gChangeInterval);
  1854. break;
  1855. case 'D':
  1856. //
  1857. // Output data rounding up and down
  1858. //
  1859. ietResult = IsInputValid(argc,
  1860. i,
  1861. argv,
  1862. "",
  1863. NULL,
  1864. NULL,
  1865. 0,
  1866. ORDER_ANY,
  1867. OPTIONAL_ANY
  1868. );
  1869. if(ietResult == INPUT_GOOD){
  1870. FPRINTF(stdout, "---> Will output data rounding bucket addresses up and down\n");
  1871. bRoundingVerboseOutput = TRUE;
  1872. } else if (ietResult == BOGUS_ENTRY){
  1873. ExitWithUnknownOptionMessage(argv[i+1]);
  1874. } else {
  1875. ExitWithUnknownOptionMessage(argv[i]);
  1876. }
  1877. break;
  1878. case 'E':
  1879. //
  1880. // We already dealt with this command line parameter in the first loop
  1881. //
  1882. break;
  1883. case 'F':
  1884. //
  1885. // User asked to Finish processing the collected data at high priority
  1886. //
  1887. ietResult = IsInputValid(argc,
  1888. i,
  1889. argv,
  1890. "",
  1891. NULL,
  1892. NULL,
  1893. 0,
  1894. ORDER_ANY,
  1895. OPTIONAL_ANY
  1896. );
  1897. if(ietResult == INPUT_GOOD){
  1898. bProcessDataHighPriority = TRUE;
  1899. } else if (ietResult == BOGUS_ENTRY){
  1900. ExitWithUnknownOptionMessage(argv[i+1]);
  1901. } else {
  1902. ExitWithUnknownOptionMessage(argv[i]);
  1903. }
  1904. break;
  1905. case 'G':
  1906. //
  1907. // User wants interesing data statistics so we need to turn on all related sources
  1908. //
  1909. {
  1910. int IDataElements = sizeof(IData)/sizeof(KPROFILE_SOURCE);
  1911. ietResult = IsInputValid(argc,
  1912. i,
  1913. argv,
  1914. "#",
  1915. &IDataCommonRate,
  1916. NULL,
  1917. 0,
  1918. ORDER_ANY,
  1919. OPTIONAL_ANY
  1920. );
  1921. if(ietResult == INPUT_GOOD){
  1922. //
  1923. // User wants interesting data statistics and also to specify the common sampling interval
  1924. //
  1925. if (IDataCommonRate == 0) {
  1926. InvalidEntryMessage(argv[i],
  1927. argv[i+1],
  1928. "Expecting decimal value >0 of events/hit, 0 < N < 10^9, space separated",
  1929. FALSE,
  1930. TRUE
  1931. );
  1932. }
  1933. ++i;
  1934. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
  1935. //
  1936. // User wants interesting data statistics using a default common sampling interval
  1937. //
  1938. IDataCommonRate = gpProcDummy->Source[IData[0]].DesiredInterval; //Use a default interval
  1939. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  1940. ExitWithMissingEntryMessage(argv[i],
  1941. "'-g# N' option requires a decimal value >0 of events/hit, 0 < N < 10^9",
  1942. FALSE
  1943. );
  1944. } else if(ietResult == INVALID_NUMBER) {
  1945. InvalidEntryMessage(argv[i],
  1946. argv[i+1],
  1947. "Expecting a decimal value >0 in [ms], 0 < N < 10^9",
  1948. FALSE,
  1949. TRUE
  1950. );
  1951. } else if(ietResult == UNKNOWN_OPTION) {
  1952. ExitWithUnknownOptionMessage(argv[i]);
  1953. }
  1954. if ( IDataElements > 1 && gpProcDummy->Source[IData[0]].DesiredInterval > 0) {
  1955. for (j=1; j < IDataElements; j++) {
  1956. if ( gpProcDummy->Source[IData[j]].DesiredInterval > 0 )
  1957. IDataCommonRate = min(IDataCommonRate, gpProcDummy->Source[IData[j]].DesiredInterval);
  1958. }
  1959. FPRINTF(stdout, "---> Will attempt to set common profiling rate for interesting data statistics to %ld Events/Hit\n",
  1960. IDataCommonRate
  1961. );
  1962. for (j=0; j < IDataElements; j++) {
  1963. gpProcDummy->Source[IData[j]].Interval = IDataCommonRate;
  1964. }
  1965. bGetInterestingData = TRUE;
  1966. bOldSampling = TRUE;
  1967. FPRINTF(stdout, "---> Using Cyclic Sampling Scheme (Profiling One Source At A time)\n");
  1968. } else {
  1969. FPRINTF(stderr, "\nKERNRATE: Interesting processor-counters statistics cannot be collected on this machine\n");
  1970. }
  1971. }
  1972. break;
  1973. case 'I':
  1974. {
  1975. // We'll consider -I option as global for all processes to be profiled
  1976. // User may have put -I on the command line before any process profile source has been initialized
  1977. // We therefore supply a valid pointer to just get and store the information for later use
  1978. ULONG rate;
  1979. BOOL found;
  1980. ietResult = IsInputValid(argc,
  1981. i,
  1982. argv,
  1983. "#",
  1984. &rate,
  1985. tmpName, //Just used as a temp storage
  1986. USER_SYMPATH_LENGTH, //with long enough space
  1987. ORDER_ANY,
  1988. OPTIONAL_ANY
  1989. );
  1990. if(ietResult == INPUT_GOOD){
  1991. //
  1992. // Standard option processing (both name and rate present)
  1993. //
  1994. i += 2; // two parameters exist (number and string)
  1995. } else if(ietResult == MISSING_PARAMETER){
  1996. ExitWithMissingEntryMessage(argv[i],
  1997. "'-i Source_Name Rate' option requires at least a source name or a rate value (or both), space separated",
  1998. TRUE
  1999. );
  2000. } else if(ietResult == MISSING_STRING) { //allowed
  2001. //
  2002. // The user can specify '-i' with a rate only.
  2003. // In this case, SOURCE_TIME is used.
  2004. //
  2005. if ( rate == 0 ) {
  2006. SourcesSoFar -= 1; //Default was 1
  2007. }
  2008. gpProcDummy->Source[SOURCE_TIME].Interval = rate;
  2009. ++i; // one parameter exists (number)
  2010. break; // We are done here
  2011. } else if(ietResult == MISSING_NUMBER) { //alowed
  2012. ++i; // one parameter exists (string)
  2013. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  2014. ExitWithMissingEntryMessage(argv[i],
  2015. "'-i# Source_Name Rate' option requires a rate value for the source interval, 0 < N < 10^9 (Source_Name optional)",
  2016. FALSE
  2017. );
  2018. } else if(ietResult == INVALID_NUMBER) {
  2019. InvalidEntryMessage(argv[i+1],
  2020. argv[i+2],
  2021. "'-i Source_Name Rate' - Invalid source interval, expecting a number 0 < N < 10^9, space separated",
  2022. FALSE,
  2023. TRUE
  2024. );
  2025. } else if(ietResult == UNKNOWN_OPTION) {
  2026. ExitWithUnknownOptionMessage(argv[i]);
  2027. }
  2028. //
  2029. // Standard option processing:
  2030. // The source shortname string is specified. If not followed by a rate amount
  2031. // we'll assume the user wants the default rate
  2032. found = FALSE;
  2033. for ( ProfileSourceIndex = 0; ProfileSourceIndex < gSourceMaximum; ProfileSourceIndex++) {
  2034. if ( !_stricmp(gpProcDummy->Source[ProfileSourceIndex].ShortName, tmpName) ) {
  2035. if (ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) { // If no rate specified,
  2036. gpProcDummy->Source[ProfileSourceIndex].Interval = gpProcDummy->Source[ProfileSourceIndex].DesiredInterval;
  2037. } else {
  2038. gpProcDummy->Source[ProfileSourceIndex].Interval = rate;
  2039. }
  2040. if ( (ProfileSourceIndex > SOURCE_TIME) && (gpProcDummy->Source[ProfileSourceIndex].Interval > 0) ) {
  2041. SourcesSoFar += 1;
  2042. } else if ( (ProfileSourceIndex == SOURCE_TIME) && (gpProcDummy->Source[ProfileSourceIndex].Interval == 0) ) {
  2043. SourcesSoFar -= 1; //Start value was 1 by default
  2044. }
  2045. found = TRUE;
  2046. break;
  2047. }
  2048. }
  2049. if ( found == FALSE) {
  2050. InvalidEntryMessage(argv[i-1],
  2051. argv[i],
  2052. "Invalid source name, or not space separated\nRun KERNRATE with the '-lx' option to list supported sources",
  2053. FALSE,
  2054. TRUE
  2055. );
  2056. }
  2057. }
  2058. break;
  2059. case 'J':
  2060. //
  2061. // User specified symbol search path.
  2062. // It is going to be prepend to the default image help symbol search path.
  2063. //
  2064. ietResult = IsInputValid(argc,
  2065. i,
  2066. argv,
  2067. "",
  2068. NULL,
  2069. gUserSymbolPath,
  2070. USER_SYMPATH_LENGTH-1,
  2071. ORDER_ANY,
  2072. OPTIONAL_NONE
  2073. );
  2074. if(ietResult == INPUT_GOOD){
  2075. if( lstrlen(argv[i+1]) > USER_SYMPATH_LENGTH){
  2076. FPRINTF(stderr, "\n===>WARNING: Command-line specified symbol path length exceeds %d characters and will be truncated\n",
  2077. USER_SYMPATH_LENGTH-1
  2078. );
  2079. }
  2080. ++i;
  2081. } else if(ietResult == MISSING_PARAMETER ||
  2082. ietResult == MISSING_STRING || ietResult == MISSING_REQUIRED_STRING) {
  2083. ExitWithMissingEntryMessage(argv[i],
  2084. "'-j SymbolPath' option requires a \"SymbolPath\", space separated",
  2085. FALSE
  2086. );
  2087. } else if(ietResult == UNKNOWN_OPTION) {
  2088. ExitWithUnknownOptionMessage(argv[i]);
  2089. }
  2090. break;
  2091. case 'K':
  2092. //
  2093. // User wants to limit the output to modules that have at least MinHitsToDisplay hits
  2094. //
  2095. ietResult = IsInputValid(argc,
  2096. i,
  2097. argv,
  2098. "#",
  2099. &gMinHitsToDisplay,
  2100. NULL,
  2101. 0,
  2102. ORDER_ANY,
  2103. OPTIONAL_NONE
  2104. );
  2105. if(ietResult == INPUT_GOOD){
  2106. if ( gMinHitsToDisplay == 0 ) {
  2107. InvalidEntryMessage(argv[i],
  2108. argv[i+1],
  2109. "Invalid entry for this command line option, (expecting a decimal number > 0)",
  2110. FALSE,
  2111. TRUE
  2112. );
  2113. }
  2114. ++i;
  2115. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) {
  2116. ExitWithMissingEntryMessage(argv[i],
  2117. "'-k N' or '-k# N' options require a number for the minimum hits to display, space separated",
  2118. FALSE
  2119. );
  2120. } else if(ietResult == INVALID_NUMBER) {
  2121. InvalidEntryMessage(argv[i],
  2122. argv[i+1],
  2123. "Invalid entry for minimum hits to display (expecting a number 0 < N < 10^9)",
  2124. FALSE,
  2125. TRUE
  2126. );
  2127. } else if(ietResult == UNKNOWN_OPTION) {
  2128. ExitWithUnknownOptionMessage(argv[i]);
  2129. }
  2130. FPRINTF(stdout, "---> Minimum Number of Hits To Display in the Output Set to %u\n", gMinHitsToDisplay);
  2131. break;
  2132. case 'L':
  2133. {
  2134. // User may have put -L on the command line before any process profile source has been initialized
  2135. // We'll therefore supply a valid pointer to just get the information
  2136. PSOURCE src;
  2137. ietResult = IsInputValid(argc,
  2138. i,
  2139. argv,
  2140. "x",
  2141. NULL,
  2142. NULL,
  2143. 0,
  2144. ORDER_ANY,
  2145. OPTIONAL_ANY
  2146. );
  2147. if(ietResult == INPUT_GOOD){ //Fall through
  2148. } else if (ietResult == BOGUS_ENTRY){
  2149. ExitWithUnknownOptionMessage(argv[i+1]);
  2150. } else {
  2151. ExitWithUnknownOptionMessage(argv[i]);
  2152. }
  2153. FPRINTF(stdout, "List of profile sources supported for this platform:\n\n");
  2154. FPRINTF(stdout, "%*s - %-*s - %-10s\n\n", gDescriptionMaxLen, "Name", gTokenMaxLen, "ShortName", "Interval");
  2155. //
  2156. // Print all possible sources.
  2157. //
  2158. for ( ProfileSourceIndex = 0; ProfileSourceIndex < gSourceMaximum; ProfileSourceIndex++ ) {
  2159. ULONG OldInterval = 0;
  2160. ULONG ThisInterval = 0;
  2161. src = &gpProcDummy->Source[ProfileSourceIndex];
  2162. //
  2163. // Display the supported profile sources, only.
  2164. // We'll determine if a source is supported by trying to set its interval rate
  2165. //
  2166. Status = NtQueryIntervalProfile( src->ProfileSource, &OldInterval );
  2167. if( NT_SUCCESS(Status) ) {
  2168. NtSetIntervalProfile( src->DesiredInterval, src->ProfileSource );
  2169. Status = NtQueryIntervalProfile( src->ProfileSource, &ThisInterval );
  2170. if( NT_SUCCESS(Status) && ThisInterval > 0 ) {
  2171. if ( src->DesiredInterval ) {
  2172. FPRINTF(stdout, "%*s - %-*s - %-10ld\n",
  2173. gDescriptionMaxLen,
  2174. src->Name,
  2175. gTokenMaxLen,
  2176. src->ShortName,
  2177. src->DesiredInterval
  2178. );
  2179. }
  2180. NtSetIntervalProfile( OldInterval, src->ProfileSource );
  2181. }
  2182. }
  2183. }
  2184. FPRINTF(stdout, "\nNOTE: Only up to %u sources can be turned on simultaneously on this machine.\n",
  2185. gMaxSimultaneousSources
  2186. );
  2187. FPRINTF(stdout, " This always includes the default source (TIME).\n");
  2188. FPRINTF(stdout, " A cyclic mode of profiling will be turned on automatically if more sources are specified.\n");
  2189. #if !defined(_AMD64_)
  2190. if( gMaxSimultaneousSources > 1 )
  2191. FPRINTF(stdout, " There is no guarantee that all sources specified in the combination will work together.\n");
  2192. #endif
  2193. FPRINTF(stdout, " One can always force a cyclic mode of profiling (switching between sources) by using the\n");
  2194. FPRINTF(stdout, " '-c' command line option. This will guarantee that all specified sources will run.\n");
  2195. FPRINTF(stdout, " The run time will then be divided equally between (number of sources)*(number of processes.\n");
  2196. //
  2197. // If the user specified '-lx', we exit immediately.
  2198. //
  2199. if ( strchr(argv[i], 'x') || strchr(argv[i], 'X') ) {
  2200. exit(0);
  2201. }
  2202. FPRINTF(stdout, "\n");
  2203. }
  2204. break;
  2205. case 'M':
  2206. //
  2207. // We already dealt with this command line parameter in the first loop
  2208. // Just update the index if extra parameter was found
  2209. //
  2210. if(gAffinityMask != 0)++i;
  2211. break;
  2212. case 'N':
  2213. //
  2214. // Monitor a given process Name
  2215. //
  2216. ietResult = IsInputValid(argc,
  2217. i,
  2218. argv,
  2219. "v#",
  2220. &MaxProcSameName,
  2221. tmpName,
  2222. PROCESS_NAME_SIZE,
  2223. ORDER_NUMBER_FIRST,
  2224. OPTIONAL_NUMBER
  2225. );
  2226. //
  2227. // User wants the full list of running processes
  2228. //
  2229. if(ietResult == INPUT_GOOD || ietResult == MISSING_NUMBER){
  2230. if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ){
  2231. tlistVerbose = TRUE;
  2232. bIncludeProcessThreadsInfo = TRUE;
  2233. }
  2234. }
  2235. if(ietResult == INPUT_GOOD){
  2236. //
  2237. // User wants to specify a non-default maximum number of processes with same name
  2238. //
  2239. if ( MaxProcSameName == 0 ) {
  2240. InvalidEntryMessage(argv[i],
  2241. argv[i+1],
  2242. "Expecting a decimal value >0 for the maximum number of processes with the same name",
  2243. FALSE,
  2244. TRUE
  2245. );
  2246. }
  2247. FPRINTF(stdout, "---> Maximum monitored processes with same name set to= %ld\n", MaxProcSameName);
  2248. i += 2; // two parameters exist (number and string)
  2249. } else if(ietResult == MISSING_PARAMETER ||
  2250. ietResult == MISSING_STRING || ietResult == MISSING_REQUIRED_STRING) {
  2251. ExitWithMissingEntryMessage(argv[i],
  2252. "'-n process_name' option requires at least a process name, space separated",
  2253. FALSE
  2254. );
  2255. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  2256. ExitWithMissingEntryMessage(argv[i],
  2257. "'-n# number process_name' option requires a number followed by a process name, space separated",
  2258. FALSE
  2259. );
  2260. } else if(ietResult == MISSING_NUMBER) { //alowed
  2261. ++i; // one parameter exists (string)
  2262. } else if(ietResult == INVALID_NUMBER) {
  2263. InvalidEntryMessage(argv[i],
  2264. argv[i+1],
  2265. "'-n# number process_name' - Expecting a decimal value >0 for the maximum number of processes with the same name",
  2266. FALSE,
  2267. TRUE
  2268. );
  2269. } else if(ietResult == UNKNOWN_OPTION) {
  2270. ExitWithUnknownOptionMessage(argv[i]);
  2271. }
  2272. //
  2273. // Let's not bother the user with minor issues
  2274. //
  2275. if(!strstr(tmpName, ".exe"))
  2276. strncat(tmpName, ".exe", EXT_SIZE);
  2277. tmpName[PROCESS_SIZE - 1] = '\0';
  2278. Pid = (LONGLONG) 0xFFFFFFFF;
  2279. //
  2280. // The '-n' option requires taking a tlist to get the PID.
  2281. // If we already took a tlist but that's the first time thread info is required,
  2282. // we'll have to refresh it and take thread info as well
  2283. //
  2284. if ( !bTlistInitialized || (bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE) ) {
  2285. if( bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE ){
  2286. bIncludeThreadsInfo = TRUE;
  2287. }
  2288. //
  2289. // get the task list for the system
  2290. //
  2291. if ( !bTlistInitialized ){
  2292. gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
  2293. if (gTlistStart == NULL) {
  2294. FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(2)\n");
  2295. exit(1);
  2296. }
  2297. }
  2298. NumTasks = GetTaskList( gTlistStart, gMaxTasks );
  2299. bTlistInitialized = TRUE;
  2300. gNumTasksStart = NumTasks;
  2301. }
  2302. //
  2303. // There may be more than one process with the specified name
  2304. // We will limit the maximum number monitored (with same name) to MAX_PROCESS_SAME_NAME for now
  2305. //
  2306. if ( tlistDisplayed == FALSE && tlistVerbose ) {
  2307. FPRINTF(stdout, "\nRunning processes found before profile start:\n");
  2308. FPRINTF(stdout, " Pid Process\n");
  2309. FPRINTF(stdout, " ------- -----------\n");
  2310. }
  2311. for (m=0, k=0; m < NumTasks; m++) {
  2312. if ( tlistDisplayed == FALSE && tlistVerbose ) {
  2313. FPRINTF(stdout, "%12I64d %32s\n",
  2314. gTlistStart[m].ProcessId,
  2315. gTlistStart[m].ProcessName
  2316. );
  2317. }
  2318. if (_stricmp(gTlistStart[m].ProcessName, tmpName) == 0) {
  2319. Pid = gTlistStart[m].ProcessId;
  2320. FPRINTF(stdout, "\n===> Found process: %s, Pid: %I64d\n\n",
  2321. gTlistStart[m].ProcessName,
  2322. Pid
  2323. );
  2324. ProcToMonitor = InitializeProcToMonitor(Pid);
  2325. if( ProcToMonitor == NULL ){ //This process may be gone
  2326. FPRINTF(stdout, "KERNRATE: Could not initialize for specified process (PID= %12I64d)\n process may be gone or wrong PID specified\n", Pid);
  2327. continue;
  2328. }
  2329. if( bIncludeGeneralInfo ){
  2330. UpdateProcessStartInfo(ProcToMonitor,
  2331. &gTlistStart[m],
  2332. bIncludeProcessThreadsInfo
  2333. );
  2334. }
  2335. if((ULONG)(++k) >= MaxProcSameName) break;
  2336. }
  2337. }
  2338. if( tlistDisplayed == FALSE && tlistVerbose ){
  2339. FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
  2340. }
  2341. //
  2342. // Let's not print it again if there is another specified on the command line
  2343. //
  2344. if ( tlistDisplayed == FALSE && tlistVerbose ){
  2345. tlistDisplayed = TRUE;
  2346. }
  2347. tlistVerbose = FALSE;
  2348. if (Pid == (LONGLONG) 0xFFFFFFFF) {
  2349. Usage(FALSE);
  2350. FPRINTF(stderr, "\n===>KERNRATE: Requested Process '%s' Not Found.\n\n", argv[i]);
  2351. FPRINTF(stderr, "Kernrate could not find this process in the task list. Either it does not exist or \n");
  2352. FPRINTF(stderr, "Kernrate hit its limit of maximum %d processes in the task list\n", DEFAULT_MAX_TASKS);
  2353. FPRINTF(stderr, "In the latter case you may want to specify the process by PID instead of by name\n");
  2354. FPRINTF(stderr, "or use the '-t' option to specify a larger maximum number (default is 256)\n");
  2355. FPRINTF(stderr, "This may also happen if you specified a number for the maximum number of processes\n");
  2356. FPRINTF(stderr, "with the same name but forgot to add the process name\n");
  2357. exit(1);
  2358. }
  2359. break;
  2360. case 'O':
  2361. {
  2362. //
  2363. // Create and monitor a given process Name
  2364. //
  2365. ULONG MaxProcToCreate;// = 1;
  2366. ULONG n;
  2367. STARTUPINFO StartupInfo;
  2368. PROCESS_INFORMATION ProcessInformation;
  2369. PCHAR tmpCmdLine;
  2370. PLONGLONG PidArray;
  2371. ULONG nFound = 0;
  2372. BOOL fInheritHandles = FALSE;
  2373. ietResult = IsInputValid(argc,
  2374. i,
  2375. argv,
  2376. "v#",
  2377. &MaxProcToCreate,
  2378. tmpName,
  2379. USER_SYMPATH_LENGTH - EXT_SIZE - 1, // We must allow a fully qualified path here
  2380. ORDER_NUMBER_FIRST,
  2381. OPTIONAL_NUMBER
  2382. );
  2383. //
  2384. // User wants the full list of running processes
  2385. //
  2386. if(ietResult == INPUT_GOOD || ietResult == MISSING_NUMBER){
  2387. if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ){
  2388. tlistVerbose = TRUE;
  2389. bIncludeProcessThreadsInfo = TRUE;
  2390. bIncludeThreadsInfo = TRUE;
  2391. }
  2392. }
  2393. if(ietResult == INPUT_GOOD){
  2394. //
  2395. // User wants to specify a non-default maximum number of processes to create
  2396. //
  2397. if ( MaxProcToCreate == 0 ) {
  2398. InvalidEntryMessage(argv[i],
  2399. argv[i+1],
  2400. "Expecting a decimal value >0 for the maximum number of processes to create",
  2401. FALSE,
  2402. TRUE
  2403. );
  2404. }
  2405. FPRINTF(stdout, "---> Maximum processes to create set to= %ld for %s\n",
  2406. MaxProcToCreate,
  2407. tmpName
  2408. );
  2409. if(i+3 < argc && argv[i+3][0] == '{' ){ // Found an opening curly bracket (allowed)
  2410. i += 3; // three parameters exist (number, ProcessName and {Process Command Line})
  2411. } else {
  2412. i += 2; // two parameters exist (number and ProcessName)
  2413. }
  2414. } else if(ietResult == MISSING_PARAMETER ||
  2415. ietResult == MISSING_STRING || ietResult == MISSING_REQUIRED_STRING) {
  2416. ExitWithMissingEntryMessage(argv[i],
  2417. "Process name missing, the '-o number process_name {Process command line}' option requires at least a process Name",
  2418. FALSE
  2419. );
  2420. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  2421. ExitWithMissingEntryMessage(argv[i],
  2422. "'-o# number process_name {Process command line}' option requires a number followed by a process Name",
  2423. FALSE
  2424. );
  2425. } else if(ietResult == MISSING_NUMBER) { //alowed
  2426. MaxProcToCreate = 1;
  2427. ++i; // one parameter exists (string)
  2428. } else if(ietResult == INVALID_NUMBER) {
  2429. if(!strchr(argv[i], '#') && (i+2 < argc && argv[i+2][0] == '{') ){ // Found an opening curly bracket (allowed)
  2430. MaxProcToCreate = 1;
  2431. i += 2; // two parameters exist (two strings)
  2432. } else {
  2433. if( !strchr(argv[i], '#') && (i+1 < argc && argv[i+1][0] == '{') ){
  2434. InvalidEntryMessage(argv[i],
  2435. argv[i+1],
  2436. "'-o optional_number process_name {Optional Process command line}' - wrong order of parameters (or missing parameter) detected",
  2437. FALSE,
  2438. TRUE
  2439. );
  2440. } else {
  2441. InvalidEntryMessage(argv[i],
  2442. argv[i+1],
  2443. "'-o# number process_name {Optional Process command line}' - Expecting a decimal value >0",
  2444. FALSE,
  2445. TRUE
  2446. );
  2447. }
  2448. }
  2449. } else if(ietResult == UNKNOWN_OPTION) {
  2450. ExitWithUnknownOptionMessage(argv[i]);
  2451. }
  2452. ZeroMemory( &StartupInfo, sizeof(StartupInfo) );
  2453. StartupInfo.cb = sizeof(StartupInfo);
  2454. StartupInfo.wShowWindow = SW_SHOWDEFAULT;
  2455. //
  2456. // Let's not bother the user with minor issues
  2457. //
  2458. if( !strstr(tmpName, ".exe") && !strstr(tmpName, ".EXE") )
  2459. strncat(tmpName, ".exe", EXT_SIZE);
  2460. tmpName[USER_SYMPATH_LENGTH - 1] = '\0';
  2461. if( i < argc && argv[i][0] == '{'){ // Found an opening curly bracket
  2462. PCHAR InitPos = &argv[i][0];
  2463. PCHAR curptr = InitPos;
  2464. PCHAR ClosingBracket;
  2465. while(i < argc){ //Try to find the closing curly bracket
  2466. ClosingBracket = strchr(&argv[i][0], '}');
  2467. if(ClosingBracket == NULL){
  2468. curptr += (1+strlen(argv[i]));
  2469. ++i;
  2470. continue;
  2471. }else{
  2472. ClosingBracket = curptr + (ClosingBracket - &argv[i][0]);
  2473. break;
  2474. }
  2475. }
  2476. if(ClosingBracket != NULL){ //Process the command line found between the curly brackets
  2477. PCHAR tmp;
  2478. ULONG MaxCount;
  2479. ULONG nChars = (ULONG)(ClosingBracket - InitPos) -1; //Skip the brackets
  2480. tmpCmdLine = calloc(1, strlen(tmpName)+ sizeof(CHAR)+ (1+nChars)*sizeof(CHAR)); //name + space + cmdline + terminator
  2481. if(tmpCmdLine == NULL){
  2482. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for created process command line\n");
  2483. exit(1);
  2484. }
  2485. strncpy(tmpCmdLine, tmpName, strlen(tmpName));
  2486. strncat(tmpCmdLine, " ", 1);
  2487. memcpy(&tmpCmdLine[strlen(tmpCmdLine)], InitPos + 1, nChars); //skip opening and end brackets
  2488. tmpCmdLine[strlen(tmpName)+ nChars + 1] = '\0';
  2489. tmp = &tmpCmdLine[0];
  2490. MaxCount = strlen(tmpName) + nChars + 1;
  2491. do{ //replace any mid-cmdline string terminators with blank space character
  2492. if(*tmp == '\0')*tmp = ' ';
  2493. ++tmp;
  2494. }while(--MaxCount);
  2495. MaxCount = HandleRedirections( tmpCmdLine,
  2496. strlen(tmpName) + nChars + 1,
  2497. &ghInput,
  2498. &ghOutput,
  2499. &ghError
  2500. );
  2501. if(ghInput != NULL || ghOutput != NULL || ghError != NULL){
  2502. if ( MaxProcToCreate == 1 || (ghOutput == NULL && ghError == NULL) ){
  2503. StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
  2504. StartupInfo.hStdInput = ghInput;
  2505. StartupInfo.hStdOutput = ghOutput;
  2506. StartupInfo.hStdError = ghError;
  2507. fInheritHandles = TRUE;
  2508. } else { //We won't allow several processes to write to the same output stream simultaneously...
  2509. FPRINTF(stderr, "\nKERNRATE: Redirection of output streams in the curly brackets is not allowed if more than one\n");
  2510. FPRINTF(stderr, " process is to be created using the '-o Number ProcessName {parameters}' command line option\n");
  2511. if(ghInput != NULL)
  2512. CloseHandle(ghInput);
  2513. if(ghOutput != NULL)
  2514. CloseHandle(ghOutput);
  2515. if(ghError != NULL)
  2516. CloseHandle(ghError);
  2517. exit(1);
  2518. }
  2519. }
  2520. } else {
  2521. Usage(FALSE);
  2522. FPRINTF(stderr, "KERNRATE: Unmatched curly brackets containing the command line of the process to be created with the -o option\n");
  2523. FPRINTF(stderr, " This could also be the result of not escaping each redirection character: '<' '>' with a '^' character\n");
  2524. FPRINTF(stderr, " Note that piping '|' is not supported as part of the allowed parameters within the curly brackets\n");
  2525. exit(1);
  2526. }
  2527. } else {
  2528. tmpCmdLine = calloc(1, (1+strlen(tmpName))*sizeof(CHAR)); //name + terminator
  2529. if(tmpCmdLine == NULL){
  2530. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for created process command line\n");
  2531. exit(1);
  2532. }
  2533. strncpy(tmpCmdLine, tmpName, strlen(tmpName));
  2534. tmpCmdLine[strlen(tmpName)] = '\0';
  2535. }
  2536. PidArray = calloc(MaxProcToCreate, sizeof(ULONG));
  2537. if(PidArray == NULL){
  2538. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for created processes PID Array\n");
  2539. exit(1);
  2540. }
  2541. for (m=0; m<MaxProcToCreate; m++) {
  2542. Pid = (LONGLONG) 0xFFFFFFFF;
  2543. if(!CreateProcess(NULL, //ProcName
  2544. tmpCmdLine, //cmd line
  2545. NULL, //Security attr.
  2546. NULL, //thread attr.
  2547. fInheritHandles, //inherit handle from debugging proc
  2548. CREATE_DEFAULT_ERROR_MODE |CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
  2549. NULL, //environment of calling proc
  2550. NULL, //Current dir same as debugging proc
  2551. &StartupInfo, //Startup Info
  2552. &ProcessInformation) //PROCESS_INFORMATION struct
  2553. ) {
  2554. FPRINTF(stderr, "KERNRATE: Failed to Create Process %s\n", tmpName);
  2555. } else {
  2556. Pid = ProcessInformation.dwProcessId;
  2557. PidArray[m] = Pid;
  2558. FPRINTF(stdout, "Created Process %s, PID= %I64d\n", tmpName, Pid);
  2559. FPRINTF(stdout, "Process Command Line = %s\n", tmpCmdLine);
  2560. ProcToMonitor = InitializeProcToMonitor(Pid);
  2561. if( ProcToMonitor == NULL ){ //This process may be gone
  2562. FPRINTF(stdout, "KERNRATE: Could not initialize for specified process (PID= %12I64d)\n process may be gone or wrong PID specified\n", Pid);
  2563. continue;
  2564. }
  2565. }
  2566. }
  2567. if ( bTlistInitialized ) { //Refresh the task list to make sure we include the new process
  2568. //(this will take care of thread info as well if required for the first time)
  2569. NumTasks = GetTaskList( gTlistStart, gMaxTasks );
  2570. gNumTasksStart = NumTasks;
  2571. } else {
  2572. //
  2573. // get the task list for the system (this will take care of thread info as well if required for the first time)
  2574. //
  2575. gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
  2576. if (gTlistStart == NULL) {
  2577. FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(3)\n");
  2578. exit(1);
  2579. }
  2580. NumTasks = GetTaskList( gTlistStart, gMaxTasks );
  2581. bTlistInitialized = TRUE;
  2582. gNumTasksStart = NumTasks;
  2583. }
  2584. if ( tlistDisplayed == FALSE && tlistVerbose ) {
  2585. FPRINTF(stdout, "\nRunning processes found before profile start:\n");
  2586. FPRINTF(stdout, " Pid Process\n");
  2587. FPRINTF(stdout, " ------- -----------\n");
  2588. }
  2589. for (m=0; m < NumTasks; m++) {
  2590. if ( tlistDisplayed == FALSE && tlistVerbose ) {
  2591. FPRINTF(stdout, "%12I64d %32s\n",
  2592. gTlistStart[m].ProcessId,
  2593. gTlistStart[m].ProcessName
  2594. );
  2595. }
  2596. for (n=0; n<MaxProcToCreate; n++){
  2597. if ( gTlistStart[m].ProcessId == PidArray[n] ) {
  2598. FPRINTF(stdout, "\n===> Found process: %s, Pid: %I64d\n\n",
  2599. gTlistStart[m].ProcessName,
  2600. gTlistStart[m].ProcessId
  2601. );
  2602. nFound += 1;
  2603. ProcToMonitor = gProcessList;
  2604. while (ProcToMonitor != NULL){
  2605. if(ProcToMonitor->Pid == gTlistStart[m].ProcessId && bIncludeGeneralInfo ){
  2606. UpdateProcessStartInfo(ProcToMonitor,
  2607. &gTlistStart[m],
  2608. bIncludeProcessThreadsInfo
  2609. );
  2610. break;
  2611. }
  2612. ProcToMonitor = ProcToMonitor->Next;
  2613. }
  2614. }
  2615. }
  2616. }
  2617. if( tlistDisplayed == FALSE && tlistVerbose ){
  2618. FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
  2619. }
  2620. free(tmpCmdLine);
  2621. free(PidArray);
  2622. //
  2623. // Let's not print it again if there is another specified on the command line
  2624. //
  2625. if ( tlistDisplayed == FALSE && tlistVerbose ){
  2626. tlistDisplayed = TRUE;
  2627. }
  2628. tlistVerbose = FALSE;
  2629. if(nFound == 0){
  2630. FPRINTF(stderr, "KERNRATE: None of the processes you tried to create was found in the task list\n");
  2631. FPRINTF(stderr, " This is either because:\n");
  2632. FPRINTF(stderr, " 1. The executable was not found in the default path therefore not launched\n");
  2633. FPRINTF(stderr, " The -o option does allow you to specify a fully qualified path with the process name (use quotes)\n");
  2634. FPRINTF(stderr, " 2. The default number of entries in the task list is too small\n");
  2635. FPRINTF(stderr, " The maximum number of tasks in the task list can be increased using the -t# option\n");
  2636. FPRINTF(stderr, " 3. The processes were never created because of some other reason or are gone\n");
  2637. FPRINTF(stderr, " 4. The number or order of parameters on the command line is wrong,\n");
  2638. FPRINTF(stderr, " it should be '-o# number process_name {Process command line}' (only the process name is mandatory)\n");
  2639. exit(1);
  2640. } else if (nFound < MaxProcToCreate){
  2641. FPRINTF(stderr, "KERNRATE: Only %d of the %d processes you tried to create were found in the task list\n", nFound, MaxProcToCreate);
  2642. FPRINTF(stderr, " This is either because the default number of entries in the task list is too small\n");
  2643. FPRINTF(stderr, " or because these processes were never created or are gone\n");
  2644. FPRINTF(stderr, " The maximum number of tasks in the task list can be increased using the -t# option\n");
  2645. FPRINTF(stderr, " Kernrate will try to continue the run with the existing processes\n");
  2646. }
  2647. bWaitCreatedProcToSettle = TRUE;
  2648. }
  2649. break;
  2650. case 'P':
  2651. //
  2652. // Monitor a given process ID
  2653. //
  2654. {
  2655. LONG tmpPid;
  2656. BOOL bFound = FALSE;
  2657. ietResult = IsInputValid(argc,
  2658. i,
  2659. argv,
  2660. "v#",
  2661. &tmpPid,
  2662. NULL,
  2663. 0,
  2664. ORDER_ANY,
  2665. OPTIONAL_NONE
  2666. );
  2667. if(ietResult == INPUT_GOOD){
  2668. if (tmpPid == 0){
  2669. InvalidEntryMessage(argv[i],
  2670. argv[i+1],
  2671. "Invalid Process ID specified on the command line, expecting PID > 0",
  2672. FALSE,
  2673. TRUE
  2674. );
  2675. }
  2676. //
  2677. // User wants the full list of running processes
  2678. //
  2679. if( strchr(argv[i], 'v') || strchr(argv[i], 'V') ) {
  2680. tlistVerbose = TRUE;
  2681. bIncludeProcessThreadsInfo = TRUE;
  2682. }
  2683. ++i;
  2684. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) {
  2685. ExitWithMissingEntryMessage(argv[i],
  2686. "'-p# PID' option requires a decimal process ID (PID>0), space separated",
  2687. FALSE
  2688. );
  2689. } else if(ietResult == INVALID_NUMBER) {
  2690. InvalidEntryMessage(argv[i],
  2691. argv[i+1],
  2692. "Invalid Process ID specified on the command line, expecting a decimal number",
  2693. FALSE,
  2694. TRUE
  2695. );
  2696. } else if(ietResult == UNKNOWN_OPTION) {
  2697. ExitWithUnknownOptionMessage(argv[i]);
  2698. }
  2699. Pid = (LONGLONG)tmpPid;
  2700. ProcToMonitor = InitializeProcToMonitor(Pid);
  2701. if( ProcToMonitor == NULL ){ //This process may be gone
  2702. FPRINTF(stdout, "KERNRATE: Could not initialize for specified process (PID= %12I64d)\n process may be gone or wrong PID specified\n", Pid);
  2703. continue;
  2704. }
  2705. //
  2706. //Get the task list and Copy the initial performance data for the process
  2707. //Note: It is possible that the specified Pid is not on the tlist because
  2708. //of the DEFAULT_MAX_TASKS limit. We won't stop the run because of that but there will be no
  2709. //performance data for that process.
  2710. //
  2711. if( bIncludeGeneralInfo || tlistVerbose) {
  2712. if ( !bTlistInitialized || (bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE) ) {
  2713. //
  2714. // In the second check above if we already took a tlist but that's the first time thread info is required,
  2715. // we'll have to refresh it and take thread info as well
  2716. //
  2717. if( bIncludeProcessThreadsInfo == TRUE && bIncludeThreadsInfo == FALSE ){
  2718. bIncludeThreadsInfo = TRUE;
  2719. }
  2720. //
  2721. // get the task list for the system
  2722. //
  2723. if( !bTlistInitialized ){
  2724. gTlistStart = calloc(1, gMaxTasks*sizeof(TASK_LIST));
  2725. if (gTlistStart == NULL) {
  2726. FPRINTF(stderr, "\nKERNRATE: Allocation of memory for the running processes task list failed(4)\n");
  2727. exit(1);
  2728. }
  2729. }
  2730. NumTasks = GetTaskList( gTlistStart, gMaxTasks );
  2731. bTlistInitialized = TRUE;
  2732. gNumTasksStart = NumTasks;
  2733. }
  2734. }
  2735. if( bTlistInitialized && (bIncludeGeneralInfo || tlistVerbose) ) {
  2736. if ( tlistDisplayed == FALSE && tlistVerbose ) {
  2737. FPRINTF(stdout, "\nRunning processes found before profile start:\n");
  2738. FPRINTF(stdout, " Pid Process\n");
  2739. FPRINTF(stdout, " ------- -----------\n");
  2740. }
  2741. for (m=0; m < NumTasks; m++) {
  2742. if ( tlistDisplayed == FALSE && tlistVerbose) {
  2743. FPRINTF(stdout, "%12I64d %32s\n",
  2744. gTlistStart[m].ProcessId,
  2745. gTlistStart[m].ProcessName
  2746. );
  2747. }
  2748. if ( Pid == gTlistStart[m].ProcessId ) {
  2749. FPRINTF(stdout, "\n===> Found process: %s, Pid: %I64d\n\n",
  2750. gTlistStart[m].ProcessName,
  2751. Pid
  2752. );
  2753. UpdateProcessStartInfo(ProcToMonitor,
  2754. &gTlistStart[m],
  2755. bIncludeProcessThreadsInfo
  2756. );
  2757. bFound = TRUE;
  2758. if(!tlistVerbose)
  2759. break;
  2760. }
  2761. }
  2762. if( tlistDisplayed == FALSE && tlistVerbose ){
  2763. FPRINTF(stdout, "\nNOTE: The list above may be missing some or all processes created by the '-o' option\n");
  2764. //
  2765. // Let's not print it again if there is another specified on the command line
  2766. //
  2767. tlistDisplayed = TRUE;
  2768. tlistVerbose = FALSE;
  2769. }
  2770. if(!bFound){
  2771. FPRINTF(stderr, "===>KERNRATE: Process Performance Summary for PID= %I64d will not be gathered\n", Pid);
  2772. FPRINTF(stderr, "because Kernrate could not find this process in the task list.\n");
  2773. FPRINTF(stderr, "This could be due to Kernrate's limit of maximum %d processes in the task list\n", DEFAULT_MAX_TASKS);
  2774. FPRINTF(stderr, "You may use the '-t# N' option to specify a larger maximum number (default is 256)\n");
  2775. }
  2776. }
  2777. }
  2778. break;
  2779. case 'R':
  2780. //
  2781. // Turn on RAW bucket dump
  2782. //
  2783. ietResult = IsInputValid(argc,
  2784. i,
  2785. argv,
  2786. "d",
  2787. NULL,
  2788. NULL,
  2789. 0,
  2790. ORDER_ANY,
  2791. OPTIONAL_ANY
  2792. );
  2793. if(ietResult == INPUT_GOOD){
  2794. bRawData = TRUE;
  2795. //
  2796. // If the user specified '-rd', we want to output disassembly with the raw data.
  2797. //
  2798. if ( strchr(argv[i], 'd') || strchr(argv[i], 'D') ){
  2799. bRawDisasm = TRUE;
  2800. #ifndef DISASM_AVAILABLE
  2801. FPRINTF(stderr, "\n===>KERNRATE: '-rd' command line option specified but disassembly is not available at present\n");
  2802. #endif
  2803. }
  2804. } else if (ietResult == BOGUS_ENTRY){
  2805. ExitWithUnknownOptionMessage(argv[i+1]);
  2806. } else {
  2807. ExitWithUnknownOptionMessage(argv[i]);
  2808. }
  2809. break;
  2810. case 'S':
  2811. //
  2812. // Set Sleep interval
  2813. //
  2814. ietResult = IsInputValid(argc,
  2815. i,
  2816. argv,//argv[i][1],
  2817. "#",
  2818. &gSleepInterval,
  2819. NULL,
  2820. 0,
  2821. ORDER_ANY,
  2822. OPTIONAL_NONE
  2823. );
  2824. if(ietResult == INPUT_GOOD){
  2825. gSleepInterval *= 1000;
  2826. if (gSleepInterval == 0) {
  2827. InvalidEntryMessage(argv[i],
  2828. argv[i+1],
  2829. "Expecting a decimal number >0 in seconds",
  2830. FALSE,
  2831. TRUE
  2832. );
  2833. }
  2834. ++i;
  2835. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER || ietResult == MISSING_REQUIRED_NUMBER) {
  2836. ExitWithMissingEntryMessage(argv[i],
  2837. "'-s N' or '-s# N' options require a specified time >0 in seconds, space separated",
  2838. FALSE
  2839. );
  2840. } else if(ietResult == INVALID_NUMBER) {
  2841. InvalidEntryMessage(argv[i],
  2842. argv[i+1],
  2843. "Expecting a decimal number >0 in seconds",
  2844. FALSE,
  2845. TRUE
  2846. );
  2847. } else if(ietResult == UNKNOWN_OPTION) {
  2848. ExitWithUnknownOptionMessage(argv[i]);
  2849. }
  2850. break;
  2851. case 'T':
  2852. //
  2853. // We already dealt with this command line parameter in the second loop
  2854. // Just update the running index in case we found an extra optional entry
  2855. //
  2856. i += tJump;
  2857. break;
  2858. case 'U':
  2859. //
  2860. // Requests IMAGEHLP to present undecorated symbol names
  2861. //
  2862. ietResult = IsInputValid(argc,
  2863. i,
  2864. argv,
  2865. "",
  2866. NULL,
  2867. NULL,
  2868. 0,
  2869. ORDER_ANY,
  2870. OPTIONAL_ANY
  2871. );
  2872. if(ietResult == INPUT_GOOD){
  2873. gSymOptions |= SYMOPT_UNDNAME;
  2874. } else if (ietResult == BOGUS_ENTRY){
  2875. ExitWithUnknownOptionMessage(argv[i+1]);
  2876. } else {
  2877. ExitWithUnknownOptionMessage(argv[i]);
  2878. }
  2879. break;
  2880. case 'V':
  2881. //
  2882. // Verbose mode.
  2883. //
  2884. gVerbose = VERBOSE_DEFAULT;
  2885. ulVerbose = VERBOSE_DEFAULT;
  2886. ietResult = IsInputValid(argc,
  2887. i,
  2888. argv,
  2889. "#",
  2890. &ulVerbose,
  2891. NULL,
  2892. 0,
  2893. ORDER_ANY,
  2894. OPTIONAL_ANY
  2895. );
  2896. if(ietResult == INPUT_GOOD){
  2897. gVerbose |= ulVerbose;
  2898. if ( gVerbose > VERBOSE_MAX ){
  2899. FPRINTF(stderr,
  2900. "\n===>WARNING: Invalid Verbose level '-v %s' specified, or'ed verbose levels cannot exceed %d\n",
  2901. argv[i+1],
  2902. VERBOSE_MAX
  2903. );
  2904. FPRINTF(stderr,
  2905. "---> Verbose level is set to %d\n",
  2906. VERBOSE_MAX
  2907. );
  2908. gVerbose = VERBOSE_MAX;
  2909. }
  2910. ++i;
  2911. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { // Allowed
  2912. //
  2913. // No verbose level specified, using default
  2914. //
  2915. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  2916. ExitWithMissingEntryMessage(argv[i],
  2917. "'-v# N' option requires a specific verbose level",
  2918. FALSE
  2919. );
  2920. } else if(ietResult == INVALID_NUMBER) {
  2921. InvalidEntryMessage(argv[i],
  2922. argv[i+1],
  2923. "Invalid Verbose level (expecting a decimal entry)",
  2924. TRUE,
  2925. TRUE
  2926. );
  2927. } else if(ietResult == UNKNOWN_OPTION) {
  2928. ExitWithUnknownOptionMessage(argv[i]);
  2929. }
  2930. if ( gVerbose & VERBOSE_IMAGEHLP ) {
  2931. gSymOptions |= SYMOPT_DEBUG;
  2932. }
  2933. break;
  2934. case 'W':
  2935. {
  2936. LONG tmpTime;
  2937. //
  2938. // Case WP: (associated with process creation via the -O option)
  2939. // Wait for CR or a given number of seconds to allow the created processes to settle.
  2940. // This is useful in cases where the created process takes time to initialize and load modules,
  2941. // or if the user needs to interact with it before profiling.
  2942. //
  2943. // Case W:
  2944. // Wait for CR or a given number of seconds before starting the profile
  2945. // This is useful in cases where the system is very busy with the task being monitored
  2946. // One can start Kernrate ahead of the task, allow for proper initialization (symbol loading)
  2947. // and then launch the task to be monitored
  2948. //
  2949. ietResult = IsInputValid(argc,
  2950. i,
  2951. argv,
  2952. "#p",
  2953. &tmpTime,
  2954. NULL,
  2955. 0,
  2956. ORDER_ANY,
  2957. OPTIONAL_ANY
  2958. );
  2959. if(ietResult == INPUT_GOOD){
  2960. //
  2961. // Wait for a given number of seconds before continuing
  2962. //
  2963. if ( strchr(argv[i], 'p') || strchr(argv[i], 'P') ){
  2964. gSecondsToWaitCreatedProc = tmpTime; //Zero allowed
  2965. } else {
  2966. gSecondsToDelayProfile = tmpTime;
  2967. }
  2968. ++i;
  2969. } else if(ietResult == MISSING_PARAMETER || ietResult == MISSING_NUMBER) { //Allowed
  2970. //
  2971. // Wait for user to press a key before continuing
  2972. //
  2973. if ( strchr(argv[i], 'p') || strchr(argv[i], 'P') ){
  2974. bCreatedProcWaitForUserInput = TRUE;
  2975. } else {
  2976. bWaitForUserInput = TRUE;
  2977. }
  2978. } else if(ietResult == MISSING_REQUIRED_NUMBER){
  2979. ExitWithMissingEntryMessage(argv[i-1],
  2980. "'-wp# N' or '-w# N' options require a (decimal) number of seconds to wait",
  2981. FALSE
  2982. );
  2983. } else if(ietResult == INVALID_NUMBER) {
  2984. InvalidEntryMessage(argv[i],
  2985. argv[i+1],
  2986. "Expecting a decimal value in seconds, 0 < N < 10^9",
  2987. FALSE,
  2988. TRUE
  2989. );
  2990. } else if(ietResult == UNKNOWN_OPTION) {
  2991. ExitWithUnknownOptionMessage(argv[i]);
  2992. }
  2993. }
  2994. break;
  2995. case 'X':
  2996. //
  2997. // User asked for Locks information
  2998. //
  2999. bIncludeUserProcLocksInfo = TRUE;
  3000. bIncludeSystemLocksInfo = TRUE;
  3001. ietResult = IsInputValid(argc,
  3002. i,
  3003. argv,
  3004. "uk#",
  3005. &gLockContentionMinCount,
  3006. NULL,
  3007. 0,
  3008. ORDER_ANY,
  3009. OPTIONAL_ANY
  3010. );
  3011. if( strchr(argv[i], 'k') || strchr(argv[i], 'K') )
  3012. bIncludeUserProcLocksInfo = FALSE;
  3013. if( strchr(argv[i], 'u') || strchr(argv[i], 'U') )
  3014. bIncludeSystemLocksInfo = FALSE;
  3015. if(ietResult == INPUT_GOOD){
  3016. FPRINTF(stdout, "---> Minimum lock contention for processing set to= %ld\n", gLockContentionMinCount);
  3017. ++i;
  3018. } else if(ietResult == MISSING_NUMBER) { //Allowed
  3019. FPRINTF(stdout, "---> Minimum lock contention for processing set to default= %ld\n", gLockContentionMinCount);
  3020. } else if(ietResult == MISSING_REQUIRED_NUMBER || ietResult == MISSING_PARAMETER){
  3021. ExitWithMissingEntryMessage(argv[i],
  3022. "'-x# number' '-xk# number' '-xu# number' options require a number for the minimum lock-contention filtering",
  3023. FALSE
  3024. );
  3025. } else if(ietResult == INVALID_NUMBER) {
  3026. InvalidEntryMessage(argv[i],
  3027. argv[i+1],
  3028. "'-x# number', '-xk# number', '-xu# number' options expect a number 0 <= N < 10^9",
  3029. FALSE,
  3030. TRUE
  3031. );
  3032. } else if(ietResult == UNKNOWN_OPTION) {
  3033. ExitWithUnknownOptionMessage(argv[i]);
  3034. }
  3035. //
  3036. // The user didn't bother to read the usage guide and specified both k and u after the '-x'...
  3037. //
  3038. if( bIncludeUserProcLocksInfo == FALSE && bIncludeSystemLocksInfo == FALSE ){
  3039. bIncludeUserProcLocksInfo = TRUE;
  3040. bIncludeSystemLocksInfo = TRUE;
  3041. }
  3042. break;
  3043. case 'Z':
  3044. ietResult = IsInputValid(argc,
  3045. i,
  3046. argv,
  3047. "",
  3048. NULL,
  3049. tmpName, //Used as a temporary storage
  3050. cMODULE_NAME_STRLEN,
  3051. ORDER_ANY,
  3052. OPTIONAL_NONE
  3053. );
  3054. if (ietResult == INPUT_GOOD){
  3055. ZoomModule = calloc(1, MODULE_SIZE);
  3056. if (ZoomModule==NULL) {
  3057. FPRINTF(stderr, "\nAllocation of zoom module %s failed\n", argv[i]);
  3058. exit(1);
  3059. }
  3060. SetModuleName( ZoomModule, tmpName );
  3061. ZoomModule->bZoom = TRUE;
  3062. //
  3063. //For compatibility with original behaviour (when monitoring kernel only)
  3064. //We'll also use this for allowing the user to specify zoom modules common accross processes
  3065. //
  3066. if (ProcToMonitor == NULL){
  3067. ZoomModule->Next = gCommonZoomList;
  3068. gCommonZoomList = ZoomModule;
  3069. } else {
  3070. ZoomModule->Next = ProcToMonitor->ZoomList;
  3071. ProcToMonitor->ZoomList = ZoomModule;
  3072. }
  3073. bZoomSpecified = TRUE;
  3074. ++i;
  3075. } else if( ietResult == MISSING_PARAMETER ||
  3076. ietResult == MISSING_STRING ||
  3077. ietResult == MISSING_REQUIRED_STRING ) {
  3078. ExitWithMissingEntryMessage(argv[i],
  3079. "'-z modulename' option requires a module name, multiple usage is allowed\nRead the usage guide or help printout for more options",
  3080. TRUE
  3081. );
  3082. } else if(ietResult == UNKNOWN_OPTION) {
  3083. ExitWithUnknownOptionMessage(argv[i]);
  3084. }
  3085. break;
  3086. case 'H':
  3087. case '?':
  3088. //
  3089. // We don't really care about bogus trailing letters or entries here
  3090. // Print Usage string and exits
  3091. //
  3092. Usage(TRUE);
  3093. break;
  3094. default:
  3095. //
  3096. // Specify the unknown option and print the Usage string. Then exists.
  3097. //
  3098. ExitWithUnknownOptionMessage(argv[i]);
  3099. }
  3100. } else { //if ((argv[i][0] == '-') || (argv[i][0] == '/'))
  3101. Usage(FALSE);
  3102. FPRINTF(stderr,
  3103. "\n===>KERNRATE: Invalid switch %s\n",
  3104. argv[i]
  3105. );
  3106. exit(1);
  3107. }
  3108. }
  3109. //
  3110. // User asked for system resource information but has not turned on kernel profile
  3111. // The resource information depends on Kernrate initializing for the system process.
  3112. // We'll turn kernel profile on and issue a message
  3113. //
  3114. if( bIncludeSystemLocksInfo == TRUE && bCombinedProfile == FALSE){
  3115. bCombinedProfile = TRUE;
  3116. FPRINTF(stderr, "\n===>KERNRATE: User requested System resource (locks) information but did not turn on kernel profiling\n");
  3117. FPRINTF(stderr, " System resource information depends on Kernrate initializing for kernel profiling\n");
  3118. FPRINTF(stderr, " Kernel profiling will therefore be started by kernrate on behalf of the user\n");
  3119. }
  3120. //
  3121. // Both i386 and IA64 processors cannot always support turning on several profile sources simultaneously.
  3122. // This is because not every combination of profile sources can be turned on together.
  3123. // AMD64 does allow turning on up to 4 profile sources concurrently (in any combination).
  3124. // We'll automatically adapt the profiling method if the user specified more than the maximum
  3125. // simultaneous sources allowed but did not specify the '-c' option on the command line.
  3126. // Default switching interval will be used in that case.
  3127. //
  3128. if( SourcesSoFar > gMaxSimultaneousSources ){
  3129. if( bOldSampling == FALSE ){
  3130. bOldSampling = TRUE;
  3131. FPRINTF( stdout, "\nNOTE:\nThe number of sources specified is greater than the maximum that can be turned on\n");
  3132. FPRINTF( stdout, "simultaneously on this machine (%u), Kernrate will therefore profile one source at a time\n",
  3133. gMaxSimultaneousSources
  3134. );
  3135. FPRINTF( stdout, "The overall run time will be devided into segments (no. processes x no. profile sources)\n");
  3136. FPRINTF( stdout, "The interval for switching between sources and processes is currently set to %dms\n",
  3137. gChangeInterval
  3138. );
  3139. FPRINTF( stdout, "This interval can be adjusted using the '-c# N' command line option, where N is in [ms]\n\n");
  3140. }
  3141. }else{
  3142. if( SourcesSoFar > 1 ){
  3143. #if defined(_IA64_)
  3144. FPRINTF( stdout, "\nNOTE: Kernrate Will attempt to turn on simultaneously the sources specified\n");
  3145. FPRINTF( stdout, "but it is not guaranteed that just any combination of sources will work together\n");
  3146. FPRINTF( stdout, "on this machine. No hits will be recorded for any source that could not be turned on\n");
  3147. #else if defined(_AMD64_)
  3148. FPRINTF( stdout, "\nNOTE: The sources specified will be turned on simultaneously\n");
  3149. #endif // _IA64_
  3150. }
  3151. }
  3152. if ( SourcesSoFar == 0 ){
  3153. FPRINTF(stderr, "\n===>KERNRATE: User apparently turned OFF the default source (TIME), but did not specify any other valid source\n");
  3154. FPRINTF(stderr, " Kernrate needs at least one valid CPU source with non-zero rate to perform a profile\n");
  3155. FPRINTF(stderr, " Use the '-i SourceName Interval' command line option to specify a source\n");
  3156. FPRINTF(stderr, " Use the '-lx' command line option to get a list of supported CPU sources on the current platform\n");
  3157. FPRINTF(stderr, " Only general information will be available as a result of this run\n");
  3158. bOldSampling = FALSE; //To prevent early exit
  3159. }
  3160. //
  3161. // Determine supported sources and set Profile Interval as necessary
  3162. //
  3163. ProcToMonitor = gProcessList;
  3164. for (i=0; i < (LONG)gNumProcToMonitor; i++){
  3165. SetProfileSourcesRates( ProcToMonitor );
  3166. ProcToMonitor = ProcToMonitor->Next;
  3167. }
  3168. return;
  3169. } /* GetConfiguration() */
  3170. INPUT_ERROR_TYPE
  3171. IsInputValid(int argc,
  3172. int OptionIndex,
  3173. PCHAR *Option,
  3174. PCHAR AllowedTrailLetters,
  3175. PLONG AssociatedNumber,
  3176. PCHAR AssociatedString,
  3177. ULONG MaxStringLength,
  3178. INPUT_ORDER Order,
  3179. INPUT_OPTIONAL Optional
  3180. )
  3181. /*++
  3182. Routine Description:
  3183. Checks the validity of a command line input entry:
  3184. 1. Unallowed duplicate entries
  3185. 2. Bogus trailing letters
  3186. 3. Missing or bogus associated parameters
  3187. 4. Type and validity of additional parameters
  3188. Arguments:
  3189. argc - The number of command line arguments (including the kernrate process name)
  3190. OptionIndex - The current index of a command line entry
  3191. Option - A pointer to the command line entry (argv)
  3192. AllowedTrailLetters - A character string of the allowed sub-option letters that can come with an entry
  3193. (For example, the -n option can also accept -nv and or -n# so the string would be "v#"
  3194. AssociatedNumber - A pointer to optional or required data (number) to be specified with the option
  3195. AssociatedString - Same as above, but for a string
  3196. MaxStringLength - Maximum allowed characters in the associated string
  3197. Order - In case of two possible associated parameters (a number and a string), which one should come first
  3198. Optional - In case of two possible associated parameters (a number and a string), which one is optional
  3199. Input/Output
  3200. - When AssociatedNumber is not NULL the value found will be filled if it exists
  3201. - When AssociatedString is not NULL the string found will be filled in if it exists
  3202. Return Value:
  3203. - Error type/condition
  3204. --*/
  3205. {
  3206. BOOL bFoundNumber = FALSE;
  3207. BOOL bFoundString = FALSE;
  3208. int index;
  3209. LONG i;
  3210. LONG OptionLength = lstrlen(Option[OptionIndex]);
  3211. LONG TrailerLength = lstrlen(AllowedTrailLetters);
  3212. const int maxIndex = sizeof(InputOption)/sizeof(InputOption[0])-1;
  3213. const int wIndex = 'W' - 'A';
  3214. //
  3215. //Check for unallowed duplicate entries
  3216. //
  3217. index = toupper(Option[OptionIndex][1]) - 'A';
  3218. if( index < 0 || index > maxIndex ){ //Sanity check (should have been detected already in GetConfiguration)
  3219. ExitWithUnknownOptionMessage(Option[OptionIndex]);
  3220. }
  3221. //
  3222. // Deal with special case ('-w' and '-wp')
  3223. //
  3224. if(index == wIndex){
  3225. if( strchr(Option[OptionIndex], 'p') || strchr(Option[OptionIndex], 'P') ){
  3226. wpCount.ActualCount += 1;
  3227. if( wpCount.ActualCount > wpCount.Allowed ){
  3228. InputOption[index].ActualCount = InputOption[index].Allowed; //cause failure
  3229. }
  3230. } else {
  3231. wCount.ActualCount += 1;
  3232. if( wCount.ActualCount > wCount.Allowed ){
  3233. InputOption[index].ActualCount = InputOption[index].Allowed; //cause failure
  3234. }
  3235. }
  3236. }
  3237. InputOption[index].ActualCount += 1;
  3238. if( (InputOption[index].Allowed > -1) && InputOption[index].ActualCount > InputOption[index].Allowed ){
  3239. FPRINTF(stderr, "KERNRATE: ERROR - Command line option -%c (or a variant) appears more times than allowed (%d)\n",
  3240. InputOption[index].InputOption,
  3241. InputOption[index].Allowed
  3242. );
  3243. if( index == wIndex ){
  3244. FPRINTF(stderr, " (One time for the '-w' option and one time for the '-wp' option)\n");
  3245. }
  3246. exit(1);
  3247. } else if( (InputOption[index].Allowed == -2) && InputOption[index].ActualCount > 1 ){
  3248. FPRINTF(stderr, "KERNRATE: WARNING - Command line option -%c (or a variant) appears more than once (non-critical error)\n",
  3249. InputOption[index].InputOption
  3250. );
  3251. }
  3252. //
  3253. //Check for bogus trailing letters
  3254. //
  3255. if( OptionLength <= 2+TrailerLength ){
  3256. for(i=2; i < OptionLength; i++)
  3257. {
  3258. if( !strchr(AllowedTrailLetters, tolower(Option[OptionIndex][i])) ){
  3259. return (UNKNOWN_OPTION);
  3260. }
  3261. }
  3262. } else {
  3263. return (UNKNOWN_OPTION);
  3264. }
  3265. //
  3266. //Check for missing (or bogus) associated parameters following the command line option
  3267. //
  3268. if (OptionIndex+1 == argc || Option[OptionIndex+1][0] == '-' || Option[OptionIndex+1][0] == '/'){
  3269. if( (AssociatedNumber != NULL) && (AssociatedString != NULL) ){
  3270. return (MISSING_PARAMETER);
  3271. }
  3272. if( AssociatedNumber != NULL ){
  3273. if( strchr(Option[OptionIndex], '#') ){
  3274. return (MISSING_REQUIRED_NUMBER);
  3275. }
  3276. return (MISSING_NUMBER);
  3277. }
  3278. if( AssociatedString != NULL ){
  3279. return (MISSING_STRING);
  3280. }
  3281. } else {
  3282. if( (AssociatedNumber == NULL) && (AssociatedString == NULL) ){
  3283. return (BOGUS_ENTRY);
  3284. }
  3285. }
  3286. //
  3287. //Check if the next parameter is a number or a string and fill in its value
  3288. //Consider the specified order in the case of two parameters
  3289. //
  3290. if( strchr(Option[OptionIndex], '#') || (NULL != AssociatedNumber) ){
  3291. if( (IsStringANumber( Option[OptionIndex+1] )) &&
  3292. ((Order == ORDER_NUMBER_FIRST) || (Order == ORDER_ANY)) ) {
  3293. bFoundNumber = TRUE;
  3294. if( NULL == AssociatedString){
  3295. *AssociatedNumber = atol( Option[OptionIndex+1] );
  3296. }
  3297. } else if( NULL != AssociatedString ) { //Either not a number,
  3298. //or this number must be a string as indicated by the order
  3299. strncpy( AssociatedString, Option[OptionIndex+1], MaxStringLength);
  3300. AssociatedString[MaxStringLength-1] = '\0';
  3301. bFoundString = TRUE;
  3302. } else {
  3303. return (INVALID_NUMBER); //We get here only if we did not expect a string but got one
  3304. }
  3305. } else if ( NULL != AssociatedString ) {
  3306. strncpy( AssociatedString, Option[OptionIndex+1], MaxStringLength);
  3307. AssociatedString[MaxStringLength-1] = '\0';
  3308. bFoundString = TRUE;
  3309. }
  3310. //
  3311. //Check if there is another parameter in continuation, if two parameters are asked for
  3312. //
  3313. if( (AssociatedNumber != NULL) && (AssociatedString != NULL) ){
  3314. if ( OptionIndex+2 == argc || Option[OptionIndex+2][0] == '-' || Option[OptionIndex+2][0] == '/' ){
  3315. //
  3316. // There is no second parameter
  3317. //
  3318. if(bFoundNumber){
  3319. if( (Optional == OPTIONAL_NUMBER) ) { //First parameter a number but it should be taken as a string
  3320. strncpy( AssociatedString, Option[OptionIndex+1], MaxStringLength);
  3321. AssociatedString[MaxStringLength-1] = '\0';
  3322. if( strchr(Option[OptionIndex], '#') ){
  3323. return (MISSING_REQUIRED_NUMBER);
  3324. }
  3325. return (MISSING_NUMBER);
  3326. } else {
  3327. *AssociatedNumber = atol( Option[OptionIndex+1] ); //Already checked if it is a valid number
  3328. if( ((Optional != OPTIONAL_STRING) && (Optional != OPTIONAL_ANY)) ){
  3329. return (MISSING_REQUIRED_STRING);
  3330. } else {
  3331. return (MISSING_STRING);
  3332. }
  3333. }
  3334. } else { //Found a string as first parameter
  3335. if( strchr(Option[OptionIndex], '#') ||
  3336. ((Optional != OPTIONAL_NUMBER) && (Optional != OPTIONAL_ANY)) ){
  3337. return (MISSING_REQUIRED_NUMBER);
  3338. }
  3339. return (MISSING_NUMBER);
  3340. }
  3341. } else if (bFoundString){ //Found a string as first parameter
  3342. if( IsStringANumber( Option[OptionIndex+2] ) ){
  3343. *AssociatedNumber = atol( Option[OptionIndex+2] );
  3344. } else {
  3345. return (INVALID_NUMBER);
  3346. }
  3347. } else { //Found a number as first parameter
  3348. *AssociatedNumber = atol( Option[OptionIndex+1] ); //Already checked if it is a valid number
  3349. strncpy( AssociatedString, Option[OptionIndex+2], MaxStringLength );
  3350. AssociatedString[MaxStringLength-1] = '\0';
  3351. }
  3352. }
  3353. return (INPUT_GOOD);
  3354. }// IsInputValid()
  3355. VOID
  3356. ExitWithUnknownOptionMessage(PCHAR CurrentOption)
  3357. {
  3358. Usage(FALSE);
  3359. FPRINTF(stderr,
  3360. "KERNRATE: Unknown command line option '%s' <---Check for missing space separator or bogus characters/entries\n",
  3361. CurrentOption
  3362. );
  3363. exit(1);
  3364. }
  3365. VOID
  3366. InvalidEntryMessage(
  3367. PCHAR CurrentOption,
  3368. PCHAR CurrentValue,
  3369. PCHAR Remark,
  3370. BOOL bUsage,
  3371. BOOL bExit
  3372. )
  3373. {
  3374. if(bUsage)
  3375. Usage(FALSE);
  3376. FPRINTF(stderr,
  3377. "\n===>KERNRATE: Invalid entry %s %s \n%s\n",
  3378. CurrentOption,
  3379. CurrentValue,
  3380. Remark
  3381. );
  3382. if(bExit)
  3383. exit(1);
  3384. }
  3385. VOID
  3386. ExitWithMissingEntryMessage(
  3387. PCHAR CurrentOption,
  3388. PCHAR Remark,
  3389. BOOL bUsage
  3390. )
  3391. {
  3392. if(bUsage)
  3393. Usage(FALSE);
  3394. FPRINTF(stderr,
  3395. "\n===>KERNRATE: Missing entry for command line option %s \n%s\n",
  3396. CurrentOption,
  3397. Remark
  3398. );
  3399. exit(1);
  3400. }
  3401. PPROC_TO_MONITOR
  3402. InitializeProcToMonitor(LONGLONG Pid)
  3403. /*++
  3404. Routine Description:
  3405. Opens the given process and gets a handle to it
  3406. Allocates a process structure and initializes it
  3407. Initializes the profile source information for this process
  3408. Arguments:
  3409. Pid - The process ID (PID)
  3410. Return Value:
  3411. - Pointer to the process structure
  3412. --*/
  3413. {
  3414. HANDLE SymHandle;
  3415. PPROC_TO_MONITOR ProcToMonitor;
  3416. PMODULE tmpModule, ZoomModule;
  3417. SymHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, //PROCESS_ALL_ACCESS,
  3418. FALSE,
  3419. (DWORD)Pid);
  3420. if (SymHandle==NULL) {
  3421. FPRINTF(stderr,
  3422. "KERNRATE: OpenProcess Pid= (%I64d) failed - it could be just gone %d\n",
  3423. Pid,
  3424. GetLastError()
  3425. );
  3426. return (NULL);
  3427. }
  3428. ProcToMonitor = calloc(1, sizeof(PROC_TO_MONITOR));
  3429. if (ProcToMonitor==NULL) {
  3430. FPRINTF(stderr, "\n===>KERNRATE: Allocation for Process %I64d failed\n", Pid);
  3431. exit(1);
  3432. }
  3433. ProcToMonitor->ProcessHandle = SymHandle;
  3434. ProcToMonitor->Pid = Pid;
  3435. ProcToMonitor->Index = gNumProcToMonitor;
  3436. ProcToMonitor->ProcessName = ""; //Will be set in GetProcessModuleInformation
  3437. ProcToMonitor->ModuleCount = 0;
  3438. ProcToMonitor->ZoomCount = 0;
  3439. ProcToMonitor->ModuleList = NULL;
  3440. ProcToMonitor->ZoomList = NULL;
  3441. ProcToMonitor->Source = NULL;
  3442. //MC
  3443. ProcToMonitor->JITHeapLocationsStart = NULL;
  3444. ProcToMonitor->JITHeapLocationsStop = NULL;
  3445. //MC
  3446. ProcToMonitor->pProcThreadInfoStart = NULL;
  3447. gNumProcToMonitor++;
  3448. //
  3449. //copy the common module list to the current process zoom list
  3450. //
  3451. tmpModule = gCommonZoomList;
  3452. while( tmpModule != NULL ){
  3453. ZoomModule = calloc(1, MODULE_SIZE);
  3454. if (ZoomModule==NULL) {
  3455. FPRINTF(stderr, "Allocation of memory for common zoom list failed\n");
  3456. exit(1);
  3457. }
  3458. strncpy(ZoomModule->module_Name, tmpModule->module_Name, cMODULE_NAME_STRLEN-1);
  3459. ZoomModule->module_Name[cMODULE_NAME_STRLEN-1] = '\0';
  3460. ZoomModule->bZoom = TRUE;
  3461. ZoomModule->Next = ProcToMonitor->ZoomList;
  3462. ProcToMonitor->ZoomList = ZoomModule;
  3463. tmpModule = tmpModule->Next;
  3464. }
  3465. //
  3466. //Initialize Profile Source Info for this process
  3467. //
  3468. InitializeProfileSourceInfo(ProcToMonitor);
  3469. ProcToMonitor->Next = gProcessList;
  3470. gProcessList = ProcToMonitor;
  3471. return (ProcToMonitor);
  3472. }// InitializeProcToMonitor()
  3473. VOID
  3474. UpdateProcessStartInfo(
  3475. PPROC_TO_MONITOR ProcToMonitor,
  3476. PTASK_LIST TaskListEntry,
  3477. BOOL bIncludeProcessThreadsInfo
  3478. )
  3479. {
  3480. if(ProcToMonitor != NULL && TaskListEntry != NULL){
  3481. memcpy(&ProcToMonitor->ProcPerfInfoStart,
  3482. &TaskListEntry->ProcessPerfInfo,
  3483. sizeof(TaskListEntry->ProcessPerfInfo)
  3484. );
  3485. if( bIncludeProcessThreadsInfo == TRUE ){
  3486. if( &TaskListEntry->pProcessThreadInfo != NULL ){
  3487. ProcToMonitor->pProcThreadInfoStart =
  3488. malloc(ProcToMonitor->ProcPerfInfoStart.NumberOfThreads*sizeof(SYSTEM_THREAD_INFORMATION));
  3489. if( ProcToMonitor->pProcThreadInfoStart == NULL){
  3490. FPRINTF(stderr, "KERNRATE: Memory allocation failed for process thread-information, attempting to continue without it\n");
  3491. return;
  3492. }
  3493. memcpy(&ProcToMonitor->pProcThreadInfoStart,
  3494. &TaskListEntry->pProcessThreadInfo,
  3495. sizeof(TaskListEntry->pProcessThreadInfo)
  3496. );
  3497. }
  3498. }
  3499. }
  3500. }
  3501. PSOURCE
  3502. InitializeStaticSources(
  3503. VOID
  3504. )
  3505. {
  3506. PSOURCE source = StaticSources;
  3507. #if defined(_IA64_)
  3508. NTSTATUS status;
  3509. PSYSTEM_PROCESSOR_INFORMATION sysProcInfo;
  3510. sysProcInfo = malloc(sizeof(SYSTEM_PROCESSOR_INFORMATION));
  3511. if(sysProcInfo == NULL){
  3512. FPRINTF(stderr,"Memory allocation failed for SystemProcessorInformation in InitializeStaticsources\n");
  3513. exit(1);
  3514. }
  3515. status = NtQuerySystemInformation( SystemProcessorInformation,
  3516. sysProcInfo,
  3517. sizeof(SYSTEM_PROCESSOR_INFORMATION),
  3518. NULL);
  3519. if ( NT_SUCCESS(status) &&
  3520. (sysProcInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) ) {
  3521. ULONG n;
  3522. switch( IA64ProcessorLevel2ProcessorFamily( sysProcInfo->ProcessorLevel ) ) {
  3523. case IA64_FAMILY_MERCED:
  3524. //
  3525. // Patch the last entry as defined with convention used to initialize
  3526. // gSourceMaximum.
  3527. //
  3528. n = sizeof(mercedStaticSources)/sizeof(mercedStaticSources[0]);
  3529. mercedStaticSources[n-1].Name = NULL;
  3530. mercedStaticSources[n-1].ShortName = "";
  3531. source = mercedStaticSources;
  3532. break;
  3533. case IA64_FAMILY_MCKINLEY:
  3534. default: // Following HALIA64 scheme, default IPF PMU as McKinley PMU.
  3535. n = sizeof(mckinleyStaticSources)/sizeof(mckinleyStaticSources[0]);
  3536. mckinleyStaticSources[n-1].Name = NULL;
  3537. mckinleyStaticSources[n-1].ShortName = "";
  3538. source = mckinleyStaticSources;
  3539. break;
  3540. }
  3541. if ( sysProcInfo != NULL ) {
  3542. free(sysProcInfo);
  3543. sysProcInfo = NULL;
  3544. }
  3545. }
  3546. #endif // _IA64_
  3547. #if defined(_AMD64_)
  3548. source = Amd64StaticSource;
  3549. #endif // _AMD64_
  3550. return source;
  3551. } // InitializeStaticSources()
  3552. ULONG
  3553. InitializeProfileSourceInfo (
  3554. PPROC_TO_MONITOR ProcToMonitor
  3555. )
  3556. /*++
  3557. Function Description:
  3558. This function initializes the Profile sources.
  3559. Argument:
  3560. Pointer to Process to be monitored or NULL if only Maximum Source Count is needed.
  3561. Return Value:
  3562. Maximum Source Count Found.
  3563. Algorithm:
  3564. ToBeSpecified
  3565. In/Out Conditions:
  3566. ToBeSpecified
  3567. Globals Referenced:
  3568. ToBeSpecified
  3569. Exception Conditions:
  3570. ToBeSpecified
  3571. MP Conditions:
  3572. ToBeSpecified
  3573. Notes:
  3574. This function has been enhanced from its original version
  3575. to support and use the static profiling sources even if the
  3576. pstat driver is not present or returned no active profiling
  3577. event.
  3578. ToDo List:
  3579. ToBeSpecified
  3580. Modification History:
  3581. 3/17/2000 TF Initial version
  3582. --*/
  3583. {
  3584. UNICODE_STRING DriverName;
  3585. NTSTATUS status;
  3586. OBJECT_ATTRIBUTES ObjA;
  3587. IO_STATUS_BLOCK IOSB;
  3588. const ULONG bufferSize = 400;
  3589. PUCHAR buffer;
  3590. ULONG i, j;
  3591. PEVENTID Event;
  3592. HANDLE DriverHandle;
  3593. PEVENTS_INFO eventInfo;
  3594. PSOURCE staticSource, src;
  3595. ULONG staticCount, driverCount, totalCount;
  3596. DriverHandle = INVALID_HANDLE_VALUE;
  3597. staticCount = driverCount = 0;
  3598. buffer = malloc(bufferSize*sizeof(UCHAR));
  3599. if(buffer == NULL){
  3600. FPRINTF(stderr,"Memory allocation failed for buffer in InitializeProfileSourceInfo\n");
  3601. exit(1);
  3602. }
  3603. //
  3604. // Try to Open PStat driver
  3605. //
  3606. RtlInitUnicodeString(&DriverName, L"\\Device\\PStat");
  3607. InitializeObjectAttributes(
  3608. &ObjA,
  3609. &DriverName,
  3610. OBJ_CASE_INSENSITIVE,
  3611. 0,
  3612. 0 );
  3613. status = NtOpenFile (
  3614. &DriverHandle, // return handle
  3615. SYNCHRONIZE | FILE_READ_DATA, // desired access
  3616. &ObjA, // Object
  3617. &IOSB, // io status block
  3618. FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
  3619. FILE_SYNCHRONOUS_IO_ALERT // open options
  3620. );
  3621. if ( NT_SUCCESS(status) ) {
  3622. //
  3623. // Determine how many events the driver provides
  3624. //
  3625. if (WIN2K_OS)
  3626. {
  3627. Event = (PEVENTID) buffer;
  3628. do {
  3629. *((PULONG) buffer) = driverCount;
  3630. driverCount += 1;
  3631. status = NtDeviceIoControlFile(
  3632. DriverHandle,
  3633. (HANDLE) NULL, // event
  3634. (PIO_APC_ROUTINE) NULL,
  3635. (PVOID) NULL,
  3636. &IOSB,
  3637. PSTAT_QUERY_EVENTS,
  3638. buffer, // input buffer
  3639. bufferSize,
  3640. NULL, // output buffer
  3641. 0
  3642. );
  3643. } while (NT_SUCCESS(status));
  3644. } else { // WinXP/.Net and above
  3645. eventInfo = (PEVENTS_INFO)buffer;
  3646. status = NtDeviceIoControlFile( DriverHandle,
  3647. (HANDLE) NULL, // event
  3648. (PIO_APC_ROUTINE) NULL,
  3649. (PVOID) NULL,
  3650. &IOSB,
  3651. PSTAT_QUERY_EVENTS_INFO,
  3652. buffer, // input buffer
  3653. bufferSize,
  3654. NULL, // output buffer
  3655. 0
  3656. );
  3657. if(NT_SUCCESS(status)) driverCount = eventInfo->Events;
  3658. if ( driverCount ) {
  3659. if ( eventInfo->TokenMaxLength > gTokenMaxLen ) {
  3660. gTokenMaxLen = eventInfo->TokenMaxLength;
  3661. }
  3662. if ( eventInfo->DescriptionMaxLength > gDescriptionMaxLen ) {
  3663. gDescriptionMaxLen = eventInfo->DescriptionMaxLength;
  3664. }
  3665. }
  3666. }
  3667. }
  3668. //
  3669. // Determine how many static events there are and
  3670. // re-initialize the format specifiers if needed.
  3671. //
  3672. src = staticSource = InitializeStaticSources();
  3673. //
  3674. // There should be at least one static source (TIME)
  3675. //
  3676. if( staticSource == NULL ) {
  3677. FPRINTF(stderr, "KERNRATE: InitializeStaticSources returned NULL, Aborting\n");
  3678. exit(1);
  3679. }
  3680. while( src->Name != NULL ) {
  3681. if ( lstrlen( src->Name ) > (LONG)gDescriptionMaxLen ) {
  3682. gDescriptionMaxLen = lstrlen( src->Name );
  3683. }
  3684. if ( strlen( src->ShortName ) > gTokenMaxLen ) {
  3685. gTokenMaxLen = lstrlen( src->ShortName );
  3686. }
  3687. staticCount++;
  3688. src++;
  3689. }
  3690. gStaticSource = staticSource;
  3691. gStaticCount = staticCount;
  3692. totalCount = driverCount + staticCount;
  3693. //
  3694. //Calling this routine with NULL will just return the maximum source count
  3695. //
  3696. if(ProcToMonitor != NULL){
  3697. //
  3698. // Allocate memory for static events, plus the driver provided events
  3699. //
  3700. ProcToMonitor->Source = calloc(totalCount, sizeof(SOURCE));
  3701. if ( ProcToMonitor->Source == NULL ) {
  3702. FPRINTF(stderr, "KERNRATE: Events memory allocation failed\n" );
  3703. if ( IsValidHandle( DriverHandle ) ) {
  3704. NtClose (DriverHandle);
  3705. }
  3706. exit(1);
  3707. }
  3708. //
  3709. // copy static events to new list
  3710. //
  3711. for (j=0; j < staticCount; j++) {
  3712. ProcToMonitor->Source[j] = staticSource[j];
  3713. }
  3714. //
  3715. // Append the driver provided events to new list
  3716. //
  3717. if ( IsValidHandle( DriverHandle ) ) {
  3718. Event = (PEVENTID) buffer;
  3719. for (i=0; i < driverCount; i++) {
  3720. *((PULONG) buffer) = i;
  3721. status = NtDeviceIoControlFile( DriverHandle,
  3722. (HANDLE) NULL, // event
  3723. (PIO_APC_ROUTINE) NULL,
  3724. (PVOID) NULL,
  3725. &IOSB,
  3726. PSTAT_QUERY_EVENTS,
  3727. buffer, // input buffer
  3728. bufferSize,
  3729. NULL, // output buffer
  3730. 0
  3731. );
  3732. //
  3733. // Source Names:
  3734. // - For the Name, we use the description
  3735. // - For the short Name, we use the token string stored
  3736. // in the first string of the buffer
  3737. //
  3738. if( NT_SUCCESS(status) ){
  3739. ProcToMonitor->Source[j].Name = _strdup ( (PCHAR)(Event->Buffer + Event->DescriptionOffset) );
  3740. ProcToMonitor->Source[j].ShortName = _strdup( (PCHAR)Event->Buffer );
  3741. ProcToMonitor->Source[j].ProfileSource = Event->ProfileSource;
  3742. ProcToMonitor->Source[j].DesiredInterval = Event->SuggestedIntervalBase;
  3743. j++;
  3744. }
  3745. } //for
  3746. } //if( IsValidHandle() )
  3747. } // if( ProcToMonitor )
  3748. if ( IsValidHandle( DriverHandle ) ){
  3749. NtClose (DriverHandle);
  3750. }
  3751. if(buffer != NULL){
  3752. free(buffer);
  3753. buffer = NULL;
  3754. }
  3755. return( totalCount );
  3756. } // InitializeProfileSourceInfo()
  3757. ULONG
  3758. NextSource(
  3759. IN ULONG CurrentSource,
  3760. IN PMODULE ModuleList,
  3761. IN PPROC_TO_MONITOR ProcToMonitor
  3762. )
  3763. /*++
  3764. Routine Description:
  3765. Stops the current profile source and starts the next one.
  3766. If a Source is already started, it will be stopped first, otherwise no source will
  3767. be stopped and the first active source will be started.
  3768. Arguments:
  3769. CurrentSource - Supplies the current profile source
  3770. ModuleList - Supplies the list of modules whose soruces are to be changed
  3771. ProcToMonitor - Pointer to the process to be monitored
  3772. Return Value:
  3773. Returns the new current profile source
  3774. --*/
  3775. {
  3776. if ( ProcToMonitor->Source[CurrentSource].bProfileStarted == TRUE) {
  3777. StopSource(CurrentSource, ModuleList, ProcToMonitor);
  3778. ProcToMonitor->Source[CurrentSource].bProfileStarted = FALSE;
  3779. }
  3780. //
  3781. // Iterate through the available sources to find the
  3782. // next active source to be started.
  3783. //
  3784. if (ModuleList->mu.bProfileStarted == FALSE) {
  3785. CurrentSource = 0;
  3786. while ( ProcToMonitor->Source[CurrentSource].Interval == 0 ){
  3787. CurrentSource = CurrentSource+1;
  3788. if (CurrentSource >= gSourceMaximum) {
  3789. FPRINTF(stderr, "\n===>KERNRATE: NextSource - No valid sources found to start profiling\n");
  3790. exit(1);
  3791. }
  3792. }
  3793. } else {
  3794. do {
  3795. CurrentSource = CurrentSource+1;
  3796. if (CurrentSource == gSourceMaximum) {
  3797. CurrentSource = 0;
  3798. }
  3799. } while ( ProcToMonitor->Source[CurrentSource].Interval == 0);
  3800. }
  3801. StartSource(CurrentSource, ModuleList, ProcToMonitor);
  3802. ProcToMonitor->Source[CurrentSource].bProfileStarted = TRUE;
  3803. return(CurrentSource);
  3804. }
  3805. VOID
  3806. StopSource(
  3807. IN ULONG ProfileSourceIndex,
  3808. IN PMODULE ModuleList,
  3809. IN PPROC_TO_MONITOR ProcToMonitor
  3810. )
  3811. /*++
  3812. Routine Description:
  3813. Stops all profile objects for a given source
  3814. Arguments:
  3815. ProfileSource - Supplies the source to be stopped.
  3816. ModuleList - Supplies the list of modules whose profiles are to be stopped
  3817. ProcToMonitor - Pointer to the process to be monitored
  3818. Return Value:
  3819. None.
  3820. --*/
  3821. {
  3822. PMODULE Current;
  3823. NTSTATUS Status;
  3824. NTSTATUS SaveStatus;
  3825. ULONGLONG StopTime;
  3826. ULONGLONG ElapsedTime;
  3827. LONG CpuNumber;
  3828. Current = ModuleList;
  3829. while (Current != NULL) {
  3830. SaveStatus = STATUS_SUCCESS;
  3831. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  3832. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  3833. Status = NtStopProfile(Current->Rate[ProfileSourceIndex].ProfileHandle[CpuNumber]);
  3834. if (!NT_SUCCESS(Status)) {
  3835. SaveStatus = Status;
  3836. FPRINTF(stderr,
  3837. "Pid= %I64d (%s) - NtStopProfile on source %s on CPU %d failed, %s %08lx",
  3838. ProcToMonitor->Pid,
  3839. ProcToMonitor->ProcessName,
  3840. ProcToMonitor->Source[ProfileSourceIndex].Name,
  3841. CpuNumber,
  3842. Current->module_Name,
  3843. Status);
  3844. if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, " - Status = NO_MEMORY");
  3845. if(Status == STATUS_PROFILING_NOT_STARTED)FPRINTF(stderr, " - Status = PROFILING_NOT_STARTED");
  3846. FPRINTF(stderr,"\n");
  3847. }
  3848. }
  3849. if (NT_SUCCESS(SaveStatus)) {
  3850. GetSystemTimeAsFileTime((LPFILETIME)&StopTime);
  3851. ElapsedTime = StopTime - Current->Rate[ProfileSourceIndex].StartTime;
  3852. Current->Rate[ProfileSourceIndex].TotalTime += ElapsedTime;
  3853. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  3854. Current->Rate[ProfileSourceIndex].TotalCount[CpuNumber] +=
  3855. Current->Rate[ProfileSourceIndex].CurrentCount[CpuNumber];
  3856. Current->Rate[ProfileSourceIndex].CurrentCount[CpuNumber] = 0;
  3857. }
  3858. }
  3859. Current = Current->Next;
  3860. }
  3861. }
  3862. VOID
  3863. StartSource(
  3864. IN ULONG ProfileSourceIndex,
  3865. IN PMODULE ModuleList,
  3866. IN PPROC_TO_MONITOR ProcToMonitor
  3867. )
  3868. /*++
  3869. Routine Description:
  3870. Starts all profile objects for a given source
  3871. Arguments:
  3872. ProfileSource - Supplies the source to be started.
  3873. ModuleList - Supplies the list of modules whose profiles are to be stopped
  3874. ProcToMonitor - Pointer to the process to be monitored
  3875. Return Value:
  3876. None.
  3877. --*/
  3878. {
  3879. PMODULE Current;
  3880. NTSTATUS Status;
  3881. LONG CpuNumber;
  3882. Current = ModuleList;
  3883. while (Current != NULL) {
  3884. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  3885. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  3886. Status = NtStartProfile(Current->Rate[ProfileSourceIndex].ProfileHandle[CpuNumber]);
  3887. if (!NT_SUCCESS(Status)) {
  3888. FPRINTF(stderr,
  3889. "Pid= %I64d (%s) - NtStartProfile on source %s for Module %s failed on CPU %d, %08lx",
  3890. ProcToMonitor->Pid,
  3891. ProcToMonitor->ProcessName,
  3892. ProcToMonitor->Source[ProfileSourceIndex].Name,
  3893. Current->module_Name,
  3894. CpuNumber,
  3895. Status);
  3896. if(Status == STATUS_PROFILING_NOT_STOPPED)FPRINTF(stderr, " - Status = PROFILING_NOT_STOPPED");
  3897. if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, " - Status = NO_MEMORY");
  3898. if(Status == STATUS_INSUFFICIENT_RESOURCES){
  3899. FPRINTF(stderr, " - Status = INSUFFICIENT_RESOURCES\n");
  3900. FPRINTF(stderr, "KERNRATE: This issue can be caused by selecting a small bucket size and/or extensive zooming\n");
  3901. FPRINTF(stderr, " You may want to try the -c command line options in this case or not use the -m option");
  3902. }
  3903. FPRINTF(stderr,"\n");
  3904. }
  3905. }
  3906. GetSystemTimeAsFileTime((LPFILETIME)&Current->Rate[ProfileSourceIndex].StartTime);
  3907. Current = Current->Next;
  3908. }
  3909. }
  3910. VOID
  3911. OutputResults(
  3912. IN FILE *Out,
  3913. IN PPROC_TO_MONITOR ProcToMonitor
  3914. )
  3915. /*++
  3916. Routine Description:
  3917. Outputs the collected data.
  3918. Arguments:
  3919. Out - Supplies the FILE * where the output should go.
  3920. ProcToMonitor - Pointer to the process to be monitored
  3921. Return Value:
  3922. None.
  3923. --*/
  3924. {
  3925. PRATE_DATA RateData;
  3926. ULONG i, ProfileSourceIndex, Index;
  3927. ULONG RoundDown;
  3928. ULONG ModuleCount;
  3929. PULONG Hits;
  3930. LONG CpuNumber;
  3931. PMODULE ModuleList = ProcToMonitor->ModuleList;
  3932. PMODULE Current;
  3933. HANDLE SymHandle = ProcToMonitor->ProcessHandle;
  3934. BOOL bAttachedToProcess = FALSE;
  3935. //
  3936. // Sum up the total buffers of any zoomed modules
  3937. //
  3938. Current = ModuleList;
  3939. while (Current != NULL) {
  3940. if (Current->bZoom) {
  3941. Current->mu.bHasHits = FALSE;
  3942. for (Index=0; Index < gTotalActiveSources; Index++) {
  3943. ProfileSourceIndex = gulActiveSources[Index];
  3944. RateData = &Current->Rate[ProfileSourceIndex];
  3945. RateData->GrandTotalCount = 0;
  3946. //
  3947. // Sum the entire profile buffer for this module/source
  3948. //
  3949. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  3950. RateData->TotalCount[CpuNumber] = 0;
  3951. Hits = RateData->ProfileBuffer[CpuNumber];
  3952. for (i=0; i < BUCKETS_NEEDED(Current->Length); i++) {
  3953. RateData->TotalCount[CpuNumber] += Hits[i];
  3954. if( Hits[i] > 0 ) Current->mu.bHasHits = TRUE;
  3955. }
  3956. RateData->GrandTotalCount += RateData->TotalCount[CpuNumber];
  3957. }
  3958. }
  3959. }
  3960. Current = Current->Next;
  3961. }
  3962. //
  3963. // Output the results ordered by profile source.
  3964. //
  3965. if(SymHandle == SYM_KERNEL_HANDLE){
  3966. ModuleCount = gKernelModuleCount;
  3967. FPRINTF(stdout, "OutputResults: KernelModuleCount = %d\n", ModuleCount);
  3968. FPRINTF(stdout, "Percentage in the following table is based on the Total Hits for the Kernel\n");
  3969. }
  3970. else{
  3971. ModuleCount = ProcToMonitor->ModuleCount;
  3972. //MC
  3973. FPRINTF(stdout, "OutputResults: ProcessModuleCount (Including Managed-Code JITs) = %d\n", ModuleCount);
  3974. FPRINTF(stdout, "Percentage in the following table is based on the Total Hits for this Process\n");
  3975. //MC
  3976. }
  3977. OutputModuleList(Out, ModuleList, ModuleCount, ProcToMonitor, NULL);
  3978. //
  3979. // For any zoomed modules, create and output a module list
  3980. // consisting of the functions in the module.
  3981. //
  3982. Current = ModuleList;
  3983. while (Current != NULL) {
  3984. if (Current->bZoom && Current->mu.bHasHits == TRUE) {
  3985. FPRINTF(stderr, "===> Processing Zoomed Module %s...\n\n", Current->module_FileName);
  3986. for (RoundDown = 0; RoundDown <= (bRoundingVerboseOutput ? 1UL:0UL); RoundDown++) {
  3987. if ( (gVerbose & VERBOSE_PROFILING) ) {
  3988. FPRINTF(stdout, "\n -------------- VERBOSE PROFILE DATA FOR ZOOMED MODULE %s ----------------\n", Current->module_FileName);
  3989. FPRINTF(stdout,
  3990. "Module Name, Parent Base Address, Module Base address, Current Bucket Index, Current Bucket Address, Total Current Bucket Hits, Per CPU Hits, Remarks\n\n"
  3991. );
  3992. }
  3993. CreateZoomedModuleList(Current, RoundDown, ProcToMonitor);
  3994. if(ProcToMonitor->ZoomList == NULL) {
  3995. FPRINTF(stdout, "No Hits or No symbols found for module %s\n", Current->module_FileName);
  3996. } else {
  3997. PMODULE Temp;
  3998. FPRINTF(Out, "\n----- Zoomed module %s (Bucket size = %d bytes, Rounding %s) --------\n",
  3999. Current->module_FileName,
  4000. gZoomBucket,
  4001. (RoundDown ? "Up" : "Down" ) );
  4002. FPRINTF(stdout, "Percentage in the following table is based on the Total Hits for this Zoom Module\n");
  4003. OutputModuleList(Out, ProcToMonitor->ZoomList, gZoomCount, ProcToMonitor, Current);
  4004. CleanZoomModuleList(ProcToMonitor); //Done with current module - prepare for next module
  4005. }
  4006. } //for rounddown/roundup loop
  4007. //
  4008. // Display the raw bucket counts for all zoomed modules
  4009. //
  4010. if (bRawData) {
  4011. OutputRawDataForZoomModule( Out, ProcToMonitor, Current );
  4012. }
  4013. //
  4014. // We are finished with processing the data for this zoom module, let's unload the associated symbol information
  4015. //
  4016. if(!SymUnloadModule64( ProcToMonitor->ProcessHandle, Current->Base))
  4017. VerbosePrint(( VERBOSE_IMAGEHLP, "Kernrate: Could Not Unload Module, Base= %p for Process %s\n",
  4018. (PVOID64)Current->Base,
  4019. ProcToMonitor->ProcessName
  4020. ));
  4021. } else {
  4022. if( Current->bZoom )
  4023. FPRINTF(stdout, "No Hits were recorded for zoom module %s\n", Current->module_FileName);
  4024. } //if(Current->bZoom)
  4025. Current = Current->Next;
  4026. } //while module loop
  4027. //MC
  4028. if( bAttachedToProcess ){
  4029. pfnDetachFromProcess();
  4030. bAttachedToProcess = FALSE;
  4031. }
  4032. //MC
  4033. return;
  4034. } // OutputResults()
  4035. BOOL
  4036. CreateZoomModuleCallback(
  4037. IN LPSTR szSymName,
  4038. IN ULONG64 Address,
  4039. IN ULONG Size,
  4040. IN PVOID Cxt
  4041. )
  4042. {
  4043. ULONG Index, ProfileSourceIndex;
  4044. BOOL HasHits;
  4045. LONG CpuNumber;
  4046. PMODULE Module;
  4047. PRATE_DATA pRate;
  4048. PPROC_TO_MONITOR ProcToMonitor;
  4049. ULONG64 i, StartIndex, EndIndex, ip, disp, HighLimit;
  4050. static ULONG64 LastIndex;
  4051. static PMODULE LastParentModule;
  4052. static PPROC_TO_MONITOR LastCxt;
  4053. HighLimit = Address + Size;
  4054. Module = malloc(MODULE_SIZE);
  4055. if (Module == NULL) {
  4056. FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate Zoom module\n");
  4057. exit(1);
  4058. }
  4059. ProcToMonitor = (PPROC_TO_MONITOR) Cxt;
  4060. Module->Base = Address;
  4061. Module->Length = Size;
  4062. Module->bZoom = FALSE;
  4063. SetModuleName( Module, szSymName );
  4064. pRate = malloc(gSourceMaximum*(RATE_DATA_FIXED_SIZE+RATE_DATA_VAR_SIZE));
  4065. if (pRate == NULL) {
  4066. FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate RateData\n");
  4067. exit(1);
  4068. }
  4069. //
  4070. // Compute range in profile buffer to sum.
  4071. //
  4072. StartIndex = (ULONG64)((Module->Base - gCallbackCurrent->Base) / gZoomBucket);
  4073. EndIndex = (Module->Base + Module->Length - gCallbackCurrent->Base) / gZoomBucket;
  4074. //
  4075. // Check if we already counted the hits for this bucket for the present module
  4076. // in case we had an address gap for the present module within the bucket itself
  4077. //
  4078. if(StartIndex == LastIndex && LastParentModule != (PMODULE)NULL &&
  4079. gCallbackCurrent == LastParentModule &&
  4080. LastCxt != (PPROC_TO_MONITOR)NULL && LastCxt == ProcToMonitor ){
  4081. for (ip=gCallbackCurrent->Base+StartIndex*gZoomBucket; ip<Address; ip+=1){
  4082. if( SymGetSymFromAddr64(ProcToMonitor->ProcessHandle, ip, &disp, gSymbol ) ){
  4083. if( 0 == strcmp(gSymbol->Name, Module->module_Name ) ){
  4084. StartIndex+=1; // We have a match, don't count this bucket for this module again
  4085. if(StartIndex > EndIndex){
  4086. HasHits = FALSE;
  4087. goto LABEL; // No hits, jump to exit
  4088. }
  4089. }
  4090. }
  4091. }
  4092. }
  4093. //
  4094. // Look for hits in the current module and sum them up
  4095. //
  4096. HasHits = FALSE;
  4097. for (Index=0; Index < gTotalActiveSources; Index++) {
  4098. ProfileSourceIndex = gulActiveSources[Index];
  4099. Module->Rate[ProfileSourceIndex] = pRate[ProfileSourceIndex];
  4100. Module->Rate[ProfileSourceIndex].StartTime = gCallbackCurrent->Rate[ProfileSourceIndex].StartTime;
  4101. Module->Rate[ProfileSourceIndex].TotalTime = gCallbackCurrent->Rate[ProfileSourceIndex].TotalTime;
  4102. Module->Rate[ProfileSourceIndex].TotalCount = calloc(gProfileProcessors, sizeof(ULONGLONG) );
  4103. if( Module->Rate[ProfileSourceIndex].TotalCount == NULL ){
  4104. FPRINTF(stderr, "KERNRATE: Memory allocation failed for TotalCount in CreateZoomModuleCallback\n");
  4105. exit(1);
  4106. }
  4107. Module->Rate[ProfileSourceIndex].DoubtfulCounts = 0;
  4108. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  4109. Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] = 0;
  4110. for (i=StartIndex; i <= EndIndex; i++) {
  4111. Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] +=
  4112. gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
  4113. }
  4114. if (Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] > 0) {
  4115. HasHits = TRUE;
  4116. }
  4117. }
  4118. }
  4119. LABEL:
  4120. //
  4121. // If the routine has hits add it to the list, otherwise
  4122. // ignore it.
  4123. //
  4124. if (HasHits) {
  4125. //
  4126. //useful verbose print for identifying shared buckets and actual bucket inhabitants, see also raw data printout
  4127. //
  4128. if ( gVerbose & VERBOSE_PROFILING ) {
  4129. ULONGLONG BucketCount, TotCount, DoubtfulCounts;
  4130. CHAR LastSymName[cMODULE_NAME_STRLEN] = "\0";
  4131. BOOL bCounted = FALSE;
  4132. PIMAGEHLP_LINE64 pLine = (PIMAGEHLP_LINE64) malloc(sizeof(IMAGEHLP_LINE64));
  4133. if ( pLine == NULL ){
  4134. FPRINTF(stderr, "KERNRATE: Memory allocation failed for pLine in CreateZoomModuleCallback\n");
  4135. exit(1);
  4136. }
  4137. pLine->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  4138. for (Index=0; Index < gTotalActiveSources; Index++) {
  4139. ProfileSourceIndex = gulActiveSources[Index];
  4140. TotCount = 0;
  4141. DoubtfulCounts = 0;
  4142. for (i=StartIndex; i <= EndIndex; i++) {
  4143. bCounted = FALSE;
  4144. BucketCount=0;
  4145. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  4146. BucketCount += gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
  4147. }
  4148. TotCount += BucketCount;
  4149. if(BucketCount > 0){
  4150. ip = gCallbackCurrent->Base + (ULONG64)(i*gZoomBucket);
  4151. //
  4152. // Print the info for the current bucket
  4153. //
  4154. FPRINTF(stdout, "%s, 0x%I64x, 0x%I64x, %I64d, 0x%I64x, %Ld",
  4155. Module->module_Name,
  4156. gCallbackCurrent->Base,
  4157. Module->Base,
  4158. i,
  4159. ip,
  4160. BucketCount
  4161. );
  4162. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  4163. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  4164. FPRINTF(stdout, ", %Ld",
  4165. gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i]
  4166. );
  4167. }
  4168. OutputLineFromAddress64( ProcToMonitor->ProcessHandle, ip, pLine );
  4169. //
  4170. // Find out if anyone else is sharing this bucket
  4171. //
  4172. disp = 0;
  4173. for(; (ip<gCallbackCurrent->Base + (ULONG64)((i+1)*gZoomBucket))&&(ip < HighLimit); ip+=1) {
  4174. if ( SymGetSymFromAddr64(ProcToMonitor->ProcessHandle, ip, &disp, gSymbol ) ) {
  4175. if (0 != strcmp( Module->module_Name, gSymbol->Name ) ){
  4176. if(!bCounted){
  4177. FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> %s+0x%I64x",
  4178. gSymbol->Name,
  4179. disp
  4180. );
  4181. OutputLineFromAddress64( ProcToMonitor->ProcessHandle, ip+disp, pLine );
  4182. DoubtfulCounts += BucketCount;
  4183. strncpy(LastSymName, gSymbol->Name, cMODULE_NAME_STRLEN-1);
  4184. LastSymName[cMODULE_NAME_STRLEN-1] = '\0';
  4185. bCounted = TRUE;
  4186. } else if( 0 != strcmp(LastSymName, gSymbol->Name ) ){
  4187. FPRINTF(stdout, "\nActual Hits Should also be Attributed to or Shared with ===> %s+0x%I64x",
  4188. gSymbol->Name,
  4189. disp
  4190. );
  4191. OutputLineFromAddress64( ProcToMonitor->ProcessHandle, ip+disp, pLine );
  4192. strncpy(LastSymName, gSymbol->Name, cMODULE_NAME_STRLEN-1);
  4193. LastSymName[cMODULE_NAME_STRLEN-1] = '\0';
  4194. }
  4195. }
  4196. } else {
  4197. //
  4198. //This should be rare (no need to increase the doubtful counts)
  4199. //
  4200. FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> UNKNOWN_SYMBOL");
  4201. }
  4202. }
  4203. FPRINTF(stdout, "\n");
  4204. }
  4205. }
  4206. //
  4207. // Print the totals for the current module
  4208. //
  4209. FPRINTF(stdout, "%s, %s - Module Total Count = %I64d, Total Doubtful or Shared Counts = %I64d\n\n",
  4210. ProcToMonitor->Source[ProfileSourceIndex].Name,
  4211. Module->module_Name,
  4212. TotCount,
  4213. DoubtfulCounts
  4214. );
  4215. Module->Rate[ProfileSourceIndex].DoubtfulCounts = DoubtfulCounts;
  4216. if( pLine != NULL ){
  4217. free(pLine);
  4218. pLine = NULL;
  4219. }
  4220. }
  4221. } // if(VERBOSE_PROFILING)
  4222. Module->Next = ProcToMonitor->ZoomList;
  4223. ProcToMonitor->ZoomList = Module;
  4224. ++gZoomCount;
  4225. } else {
  4226. //
  4227. // Cleanup
  4228. //
  4229. for (Index=0; Index < gTotalActiveSources; Index++) {
  4230. ProfileSourceIndex = gulActiveSources[Index];
  4231. if ( Module->Rate[ProfileSourceIndex].TotalCount != NULL){
  4232. free(Module->Rate[ProfileSourceIndex].TotalCount);
  4233. Module->Rate[ProfileSourceIndex].TotalCount = NULL;
  4234. }
  4235. }
  4236. if( pRate != NULL ){
  4237. free(pRate);
  4238. pRate = NULL;
  4239. }
  4240. if( Module != NULL ){
  4241. free(Module);
  4242. Module = NULL;
  4243. }
  4244. }
  4245. LastIndex = EndIndex;
  4246. LastParentModule = gCallbackCurrent;
  4247. LastCxt = ProcToMonitor;
  4248. //MC
  4249. //
  4250. //Imagehlp SymEnumerateSymbols64 returns SUCSESS even if no symbols are found
  4251. //We need an indication wheter we need to go into expensive Managed-Code symbol enumeration
  4252. //
  4253. bImageHlpSymbolFound = TRUE;
  4254. //MC
  4255. return(TRUE);
  4256. } //CreateZoomModuleCallback
  4257. BOOL
  4258. CreateJITZoomModuleCallback(
  4259. IN PWCHAR wszSymName,
  4260. IN LPSTR szSymName,
  4261. IN ULONG64 Address,
  4262. IN ULONG Size,
  4263. IN PVOID Cxt
  4264. )
  4265. {
  4266. ULONG ProfileSourceIndex, Index;
  4267. BOOL HasHits;
  4268. LONG CpuNumber;
  4269. PMODULE Module;
  4270. PRATE_DATA pRate;
  4271. PPROC_TO_MONITOR ProcToMonitor;
  4272. ULONG64 i, StartIndex, EndIndex, ip, HighLimit;
  4273. static ULONG64 LastIndex;
  4274. static PMODULE LastParentModule;
  4275. static PPROC_TO_MONITOR LastCxt;
  4276. HighLimit = Address + Size;
  4277. Module = malloc(MODULE_SIZE);
  4278. if (Module == NULL) {
  4279. FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate Zoom module\n");
  4280. exit(1);
  4281. }
  4282. ProcToMonitor = (PPROC_TO_MONITOR) Cxt;
  4283. Module->Base = Address;
  4284. Module->Length = Size;
  4285. Module->bZoom = FALSE;
  4286. SetModuleName( Module, szSymName );
  4287. pRate = malloc(gSourceMaximum*(RATE_DATA_FIXED_SIZE+RATE_DATA_VAR_SIZE));
  4288. if (pRate == NULL) {
  4289. FPRINTF(stderr, "KERNRATE: CreateZoomModuleCallback failed to allocate RateData\n");
  4290. exit(1);
  4291. }
  4292. //
  4293. // Compute range in profile buffer to sum.
  4294. //
  4295. StartIndex = (ULONG64)((Module->Base - gCallbackCurrent->Base) / gZoomBucket);
  4296. EndIndex = (Module->Base + Module->Length - gCallbackCurrent->Base) / gZoomBucket;
  4297. //
  4298. // Check if we already counted the hits for this bucket for the present module
  4299. // in case we had an address gap for the present module within the bucket itself
  4300. //
  4301. if(StartIndex == LastIndex && LastParentModule != (PMODULE)NULL &&
  4302. gCallbackCurrent == LastParentModule &&
  4303. LastCxt != (PPROC_TO_MONITOR)NULL && LastCxt == ProcToMonitor ){
  4304. for (ip=gCallbackCurrent->Base+StartIndex*gZoomBucket; ip<Address; ip+=1){
  4305. if( 0 < pfnIP2MD( (DWORD_PTR)ip, &gwszSymbol ) && (gwszSymbol != NULL) ){
  4306. if( !wcscmp( wszSymName, gwszSymbol ) ){
  4307. StartIndex += 1; // We have a match, don't count this bucket for this module again
  4308. if(StartIndex > EndIndex){
  4309. HasHits = FALSE;
  4310. goto LABEL; // No hits, jump to exit
  4311. }
  4312. }
  4313. }
  4314. }
  4315. }
  4316. //
  4317. // Look for hits in the current module
  4318. //
  4319. HasHits = FALSE;
  4320. for (Index=0; Index < gTotalActiveSources; Index++) {
  4321. ProfileSourceIndex = gulActiveSources[Index];
  4322. Module->Rate[ProfileSourceIndex] = pRate[ProfileSourceIndex];
  4323. Module->Rate[ProfileSourceIndex].StartTime = gCallbackCurrent->Rate[ProfileSourceIndex].StartTime;
  4324. Module->Rate[ProfileSourceIndex].TotalTime = gCallbackCurrent->Rate[ProfileSourceIndex].TotalTime;
  4325. Module->Rate[ProfileSourceIndex].TotalCount = calloc(gProfileProcessors, sizeof(ULONGLONG) );
  4326. if( Module->Rate[ProfileSourceIndex].TotalCount == NULL ){
  4327. FPRINTF(stderr, "KERNRATE: Memory allocation failed for TotalCount in CreateZoomModuleCallback\n");
  4328. exit(1);
  4329. }
  4330. Module->Rate[ProfileSourceIndex].DoubtfulCounts = 0;
  4331. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  4332. Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] = 0;
  4333. for (i=StartIndex; i <= EndIndex; i++) {
  4334. Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] +=
  4335. gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
  4336. }
  4337. if (Module->Rate[ProfileSourceIndex].TotalCount[CpuNumber] > 0) {
  4338. HasHits = TRUE;
  4339. }
  4340. }
  4341. }
  4342. LABEL:
  4343. //
  4344. // If the routine has hits add it to the list, otherwise
  4345. // ignore it.
  4346. //
  4347. if (HasHits) {
  4348. //
  4349. //useful verbose print for identifying shared buckets and actual bucket inhabitants, see also raw data printout
  4350. //
  4351. if ( gVerbose & VERBOSE_PROFILING ) {
  4352. ULONGLONG BucketCount, TotCount, DoubtfulCounts;
  4353. WCHAR LastSymName[cMODULE_NAME_STRLEN];
  4354. BOOL bCounted = FALSE;
  4355. PIMAGEHLP_LINE64 pLine = malloc(sizeof(IMAGEHLP_LINE64));
  4356. if ( pLine == NULL ){
  4357. FPRINTF(stderr, "KERNRATE: Memory allocation failed for pLine in CreateZoomModuleCallback\n");
  4358. exit(1);
  4359. }
  4360. pLine->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  4361. _wcsset(&LastSymName[cMODULE_NAME_STRLEN-1], L'\0');
  4362. for (Index=0; Index < gTotalActiveSources; Index++) {
  4363. ProfileSourceIndex = gulActiveSources[Index];
  4364. TotCount = 0;
  4365. DoubtfulCounts = 0;
  4366. for (i=StartIndex; i <= EndIndex; i++) {
  4367. bCounted = FALSE;
  4368. BucketCount=0;
  4369. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  4370. BucketCount += gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i];
  4371. }
  4372. TotCount += BucketCount;
  4373. if(BucketCount > 0){
  4374. ip = gCallbackCurrent->Base + (ULONG64)(i*gZoomBucket);
  4375. //
  4376. // Print the info for the current bucket
  4377. //
  4378. FPRINTF(stdout, "%s, 0x%I64x, 0x%I64x, %I64d, 0x%I64x, %Ld",
  4379. Module->module_Name,
  4380. gCallbackCurrent->Base,
  4381. Module->Base,
  4382. i,
  4383. ip,
  4384. BucketCount
  4385. );
  4386. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  4387. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  4388. FPRINTF(stdout, ", %Ld",
  4389. gCallbackCurrent->Rate[ProfileSourceIndex].ProfileBuffer[CpuNumber][i]
  4390. );
  4391. }
  4392. //
  4393. // Find out if anyone else is sharing this bucket
  4394. //
  4395. for(; (ip<gCallbackCurrent->Base + (ULONG64)((i+1)*gZoomBucket)) && (ip<HighLimit); ip+=1) {
  4396. if ( (0 < pfnIP2MD( (DWORD_PTR)ip, &gwszSymbol )) && (gwszSymbol != NULL) ) {
  4397. if (0 != wcscmp( wszSymName, gwszSymbol ) ){
  4398. if(!bCounted){
  4399. FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> %S",
  4400. gwszSymbol
  4401. );
  4402. DoubtfulCounts += BucketCount;
  4403. wcsncpy(LastSymName, gwszSymbol, cMODULE_NAME_STRLEN-1);
  4404. _wcsset(&LastSymName[cMODULE_NAME_STRLEN-1], L'\0');
  4405. bCounted = TRUE;
  4406. } else if( 0 != wcscmp(LastSymName, gwszSymbol ) ){
  4407. FPRINTF(stdout, "\nActual Hits Should also be Attributed to or Shared with ===> %S",
  4408. gwszSymbol
  4409. );
  4410. wcsncpy(LastSymName, gwszSymbol, cMODULE_NAME_STRLEN-1);
  4411. _wcsset(&LastSymName[cMODULE_NAME_STRLEN-1], L'\0');
  4412. }
  4413. }
  4414. } else {
  4415. //
  4416. //This should be rare (no need to increase the doubtful counts)
  4417. //
  4418. FPRINTF(stdout, " , Actual Hits Should be Attributed to or Shared with ===> UNKNOWN_SYMBOL");
  4419. }
  4420. }
  4421. FPRINTF(stdout, "\n");
  4422. }
  4423. }
  4424. //
  4425. // Print the totals for the current module
  4426. //
  4427. FPRINTF(stdout, "%s, %s - Module Total Count = %I64d, Total Doubtful or Shared Counts = %I64d\n\n",
  4428. ProcToMonitor->Source[ProfileSourceIndex].Name,
  4429. Module->module_Name,
  4430. TotCount,
  4431. DoubtfulCounts
  4432. );
  4433. Module->Rate[ProfileSourceIndex].DoubtfulCounts = DoubtfulCounts;
  4434. if( pLine != NULL ){
  4435. free(pLine);
  4436. pLine = NULL;
  4437. }
  4438. }
  4439. } // if(VERBOSE_PROFILING)
  4440. Module->Next = ProcToMonitor->ZoomList;
  4441. ProcToMonitor->ZoomList = Module;
  4442. ++gZoomCount;
  4443. } else {
  4444. //
  4445. // Cleanup
  4446. //
  4447. for (Index=0; Index < gTotalActiveSources; Index++) {
  4448. ProfileSourceIndex = gulActiveSources[Index];
  4449. if ( Module->Rate[ProfileSourceIndex].TotalCount != NULL){
  4450. free(Module->Rate[ProfileSourceIndex].TotalCount);
  4451. Module->Rate[ProfileSourceIndex].TotalCount = NULL;
  4452. }
  4453. }
  4454. if( pRate != NULL ){
  4455. free(pRate);
  4456. pRate = NULL;
  4457. }
  4458. if( Module != NULL ){
  4459. free(Module);
  4460. Module = NULL;
  4461. }
  4462. }
  4463. LastIndex = EndIndex;
  4464. LastParentModule = gCallbackCurrent;
  4465. LastCxt = ProcToMonitor;
  4466. return(TRUE);
  4467. } //CreateJITZoomModuleCallback;
  4468. /* BEGIN_IMS TkEnumerateSymbols
  4469. ******************************************************************************
  4470. ****
  4471. **** TkEnumerateSymbols ( )
  4472. ****
  4473. ******************************************************************************
  4474. *
  4475. * Function Description:
  4476. *
  4477. * Calls the specified function for every symbol in the Current module.
  4478. * The algorithm results in a round-up behavior for the output --
  4479. * for each bucket, the symbol corresponding to the first byte of the
  4480. * bucket is used.
  4481. *
  4482. * Arguments:
  4483. *
  4484. * IN HANDLE SymHandle : ImageHelp handle
  4485. *
  4486. * IN PMODULE Current : Pointer to current module structure
  4487. *
  4488. * IN PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback : Routine to call for each symbol
  4489. *
  4490. * IN PVOID pProc : Pointer to Process
  4491. *
  4492. * Return Value:
  4493. *
  4494. * BOOL
  4495. *
  4496. * Algorithm:
  4497. *
  4498. * ToBeSpecified
  4499. *
  4500. * Globals Referenced:
  4501. *
  4502. * ToBeSpecified
  4503. *
  4504. * Exception Conditions:
  4505. *
  4506. * ToBeSpecified
  4507. *
  4508. * In/Out Conditions:
  4509. *
  4510. * ToBeSpecified
  4511. *
  4512. * Notes:
  4513. *
  4514. * ToBeSpecified
  4515. *
  4516. * ToDo List:
  4517. *
  4518. * ToBeSpecified
  4519. *
  4520. * Modification History:
  4521. *
  4522. * 9/5/97 TF Initial version
  4523. *
  4524. ******************************************************************************
  4525. * END_IMS TkEnumerateSymbols */
  4526. BOOL
  4527. TkEnumerateSymbols(
  4528. IN HANDLE SymHandle,
  4529. IN PMODULE Current,
  4530. IN PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
  4531. IN PVOID pProc
  4532. )
  4533. {
  4534. CHAR CurrentSym[cMODULE_NAME_STRLEN];
  4535. DWORD64 CurrentAddr = 0;
  4536. ULONG i;
  4537. DWORD64 Displacement;
  4538. CurrentSym[0] = '\0';
  4539. for (i=0; i<BUCKETS_NEEDED(Current->Length); i++) {
  4540. // Check if this bucket will be assigned to a different symbol...
  4541. if (SymGetSymFromAddr64(SymHandle, Current->Base+i*gZoomBucket, &Displacement, gSymbol )) {
  4542. // It will... Invoke the callback for the old one
  4543. if (CurrentSym[0] == '\0' ||
  4544. strncmp(gSymbol->Name, CurrentSym, strlen(CurrentSym))) {
  4545. if (CurrentAddr != 0) {
  4546. ULONG64 Size = (Current->Base+i*gZoomBucket) - CurrentAddr;
  4547. if ( Size == 0 ) {
  4548. FPRINTF( stderr, "XXTF Size==0 - %s = %s\n", gSymbol->Name, CurrentSym );
  4549. }
  4550. else {
  4551. if (!EnumSymbolsCallback(CurrentSym, CurrentAddr, (ULONG)Size, pProc)) {
  4552. FPRINTF(stderr, "KERNRATE: Failed CallBack in TkEnumerateSymbols Address= %x\n",
  4553. Current->Base+i*gZoomBucket
  4554. );
  4555. break;
  4556. }
  4557. }
  4558. }
  4559. // Save the new info
  4560. CurrentAddr = Current->Base+i*gZoomBucket;
  4561. strncpy(CurrentSym, gSymbol->Name, cMODULE_NAME_STRLEN-1);
  4562. CurrentSym[ cMODULE_NAME_STRLEN-1 ] = '\0';
  4563. }
  4564. }
  4565. else {
  4566. DWORD ErrorCode = GetLastError();
  4567. FPRINTF(stderr, "KERNRATE: Failed Call to SymGetSymFromAddress %x in TkEnumerateSymbols Error Code= %x\n",
  4568. Current->Base+i*gZoomBucket,
  4569. ErrorCode
  4570. );
  4571. }
  4572. }
  4573. // Cleanup for the last symbol
  4574. if (CurrentAddr != 0) {
  4575. ULONG64 Size = (Current->Base+i*gZoomBucket) - CurrentAddr;
  4576. if( (CurrentAddr + Size) < (Current->Base + Current->Length) )
  4577. (VOID) EnumSymbolsCallback(CurrentSym, CurrentAddr, (ULONG)Size, pProc);
  4578. }
  4579. return(TRUE);
  4580. } // TkEnumerateSymbols()
  4581. BOOL
  4582. JITEnumerateSymbols(
  4583. IN PMODULE Current,
  4584. IN PVOID pProc,
  4585. IN DWORD64 BaseOptional,
  4586. IN ULONG SizeOptional
  4587. )
  4588. /*++
  4589. Routine Description:
  4590. Enumerates the symbols found in a managed code module
  4591. Arguments:
  4592. Current - Pointer to the managed code module to be enumerated for symbols
  4593. pProc - Pointer to the structure of the process being monitored
  4594. BaseOptional - If not zero then the enumeration will be performed on part of the module,
  4595. starting from this address
  4596. SizeOptional - Required size if BaseOptional is non-zero
  4597. Return Value:
  4598. TRUE if symbols are found, FALSE in case no symbols are found
  4599. --*/
  4600. {
  4601. WCHAR CurrentSym[cMODULE_NAME_STRLEN];
  4602. CHAR SymName[cMODULE_NAME_STRLEN];
  4603. ANSI_STRING AnsiString;
  4604. UNICODE_STRING UnicodeString;
  4605. DWORD64 CurrentAddr = 0;
  4606. DWORD64 TopAddress;
  4607. DWORD64 Base;
  4608. ULONG Length;
  4609. BOOL bFoundSym = FALSE;
  4610. ULONG Size = 0;
  4611. ULONG InitialStep = (gZoomBucket < JIT_MAX_INITIAL_STEP)? gZoomBucket : JIT_MAX_INITIAL_STEP;
  4612. ULONG step = InitialStep;
  4613. ULONG i, j, k;
  4614. WCHAR *Symbol = (WCHAR *)malloc ( cMODULE_NAME_STRLEN * sizeof(WCHAR) );
  4615. if (Symbol == NULL){
  4616. FPRINTF(stderr, "KERNRATE: Allocation for Symbol in JITEnumerateSymbols failed\n");
  4617. exit(1);
  4618. }
  4619. CurrentSym[0] = '\0';
  4620. Base = (BaseOptional == 0)? Current->Base : BaseOptional;
  4621. CurrentAddr = Base;
  4622. Length = (SizeOptional == 0)? Current->Length : SizeOptional;
  4623. TopAddress = (SizeOptional == 0)? Current->Base + Current->Length : BaseOptional + SizeOptional;
  4624. if (CurrentAddr == 0) {
  4625. FPRINTF(stderr, "KERNRATE: Zero base address passed to JITEnumerateSymbols for module %s\n",
  4626. Current->module_Name
  4627. );
  4628. free(Symbol);
  4629. Symbol = NULL;
  4630. return (FALSE);
  4631. }
  4632. if (Current->Length == 0) {
  4633. FPRINTF(stderr, "KERNRATE: Zero module length passed to JITEnumerateSymbols for module %s\n",
  4634. Current->module_Name
  4635. );
  4636. free(Symbol);
  4637. Symbol = NULL;
  4638. return (FALSE);
  4639. }
  4640. //
  4641. // find first symbol
  4642. //
  4643. for (i=0; i < Length; i++){
  4644. j = i;
  4645. if ( (0 < pfnIP2MD( (DWORD_PTR)CurrentAddr, &Symbol )) && (Symbol != NULL) ) {
  4646. wcsncpy(CurrentSym, Symbol, cMODULE_NAME_STRLEN-1);
  4647. _wcsset(&CurrentSym[cMODULE_NAME_STRLEN-1], L'\0');
  4648. bFoundSym = TRUE;
  4649. break;
  4650. }
  4651. ++CurrentAddr;
  4652. }
  4653. if( !bFoundSym ){
  4654. free(Symbol);
  4655. Symbol = NULL;
  4656. return (FALSE);
  4657. }
  4658. step = (InitialStep < Length)? InitialStep : 1;
  4659. Size = 1;
  4660. if( gVerbose & VERBOSE_INTERNALS )
  4661. FPRINTF(stdout, "\nJITEnumSymbols Verbose Detail: Symbol, Address Range, Size\n");
  4662. for (i=j+step; i < Length; ){
  4663. k = i;
  4664. if ( (0 == pfnIP2MD( (DWORD_PTR)(Base + i), &Symbol )) || (Symbol == NULL) ) {
  4665. wcsncpy(Symbol, L"NO_SYMBOL", cMODULE_NAME_STRLEN-1);
  4666. _wcsset(&Symbol[cMODULE_NAME_STRLEN-1], L'\0');
  4667. }
  4668. if( 0 == wcscmp( CurrentSym, Symbol) ) { //Same symbol, increase size and continue stepping
  4669. Size += step;
  4670. i = k + step;
  4671. if(i < Length)
  4672. continue;
  4673. } else { // not same symbol, decrease step and go back to find boudary
  4674. step >>=1;
  4675. if (step > 0){
  4676. i = k - step; // k was incremented by previous step so it's larger than present step
  4677. continue;
  4678. }
  4679. }
  4680. if( 0 != wcscmp(Symbol, L"NO_SYMBOL") && 0 != wcscmp(Symbol, L"\0") ){
  4681. RtlInitUnicodeString( &UnicodeString, CurrentSym );
  4682. AnsiString.Buffer = SymName;
  4683. AnsiString.MaximumLength = cMODULE_NAME_STRLEN*sizeof(CHAR);
  4684. AnsiString.Length = 0;
  4685. RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, cDONOT_ALLOCATE_DESTINATION_STRING);
  4686. SymName[AnsiString.Length] = '\0';
  4687. if ( !CreateJITZoomModuleCallback(CurrentSym, SymName, CurrentAddr, Size, pProc) ) {
  4688. FPRINTF(stderr, "KERNRATE: Failed CallBack in JITEnumerateSymbols Address= %I64x for module %s\n",
  4689. CurrentAddr,
  4690. Current->module_Name
  4691. );
  4692. free(Symbol);
  4693. Symbol = NULL;
  4694. return (FALSE);
  4695. }
  4696. }
  4697. if( gVerbose & VERBOSE_INTERNALS )
  4698. FPRINTF(stdout, "%S, 0x%p - 0x%p, 0x%lx\n", CurrentSym, (PVOID)CurrentAddr, (PVOID)(CurrentAddr+Size), Size);
  4699. wcsncpy(CurrentSym, Symbol, cMODULE_NAME_STRLEN-1); //Put the next symbol into current symbol
  4700. _wcsset(&CurrentSym[cMODULE_NAME_STRLEN-1], L'\0');
  4701. CurrentAddr += Size; //Advance to next method base
  4702. Size = 1; //Reset initial size and step for the next symbol
  4703. if ( InitialStep < (TopAddress - CurrentAddr) ){
  4704. step = InitialStep;
  4705. } else {
  4706. step = 1;
  4707. }
  4708. i = k + step;
  4709. }//for i
  4710. if (Symbol != NULL) {
  4711. free(Symbol);
  4712. Symbol = NULL;
  4713. }
  4714. return TRUE;
  4715. } //JITEnumerateSymbols()
  4716. BOOL
  4717. EnumerateSymbolsByBuckets(
  4718. IN HANDLE SymHandle,
  4719. IN PMODULE Current,
  4720. IN PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
  4721. IN PVOID pProc
  4722. )
  4723. /*++
  4724. Routine Description:
  4725. Finds buckets with hits in the current module and enumerates the symbols found in these buckets
  4726. Arguments:
  4727. SymHandle - ImageHelp handle to current process
  4728. Current - Pointer to the parent module to be enumerated for symbols
  4729. EnumSymbolsCallback - Pointer to a user supplied callback function
  4730. pProc - Pointer to the structure of the process being monitored
  4731. Return Value:
  4732. TRUE if any symbols are found, FALSE in case no symbols are found at all
  4733. --*/
  4734. {
  4735. DWORD64 Base;
  4736. ULONG Size;
  4737. ULONG i;
  4738. BOOL bRet = FALSE; //One time toggle switch (once set to TRUE it remains TRUE)
  4739. Base = Current->Base;
  4740. Size = 0;
  4741. for (i=0; i< BUCKETS_NEEDED(Current->Length); i++){
  4742. if ( HitsFound(pProc, i) ){
  4743. Size += gZoomBucket; //just increase the size of this segment by a bucket
  4744. continue;
  4745. } else if ( Size > 0 ){ //Reached end of segment where hits were found, enumerate the symbols within
  4746. if(SymHandle != NULL){
  4747. if ( TRUE == PrivEnumerateSymbols( SymHandle, Current, EnumSymbolsCallback, pProc, Base, Size ) )
  4748. bRet = TRUE;
  4749. } else {
  4750. if ( TRUE == JITEnumerateSymbols( Current, pProc, Base, Size ) )
  4751. bRet = TRUE;
  4752. }
  4753. Base += Size; //Done enumerating, shift the base to the end of the segment and reset the size
  4754. Size = 0;
  4755. }
  4756. Base += gZoomBucket; //No hits found, so further shift base of segment by one bucket and continue
  4757. }
  4758. return (bRet);
  4759. }
  4760. BOOL
  4761. PrivEnumerateSymbols(
  4762. IN HANDLE SymHandle,
  4763. IN PMODULE Current,
  4764. IN PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
  4765. IN PVOID pProc,
  4766. IN DWORD64 BaseOptional,
  4767. IN ULONG SizeOptional
  4768. )
  4769. /*++
  4770. Routine Description:
  4771. Enumerates the symbols found in a module
  4772. Arguments:
  4773. SymHandle - ImageHelp handle to current process
  4774. Current - Pointer to the module to be enumerated for symbols
  4775. EnumSymbolsCallback - Pointer to a user supplied callback function
  4776. pProc - Pointer to the structure of the process being monitored
  4777. BaseOptional - If not zero then the enumeration will be performed on part of the module,
  4778. starting from this address
  4779. SizeOptional - Required size if BaseOptional is non-zero
  4780. Return Value:
  4781. TRUE if symbols are found, FALSE in case no symbols are found
  4782. --*/
  4783. {
  4784. CHAR CurrentSym[cMODULE_NAME_STRLEN];
  4785. CHAR SymName[cMODULE_NAME_STRLEN];
  4786. DWORD64 CurrentAddr = 0;
  4787. DWORD64 TopAddress;
  4788. DWORD64 Displacement;
  4789. DWORD64 Base;
  4790. ULONG Length;
  4791. BOOL bFoundSym = FALSE;
  4792. ULONG Size = 0;
  4793. ULONG step = INITIAL_STEP;
  4794. ULONG i, j, k;
  4795. PIMAGEHLP_SYMBOL64 Symbol = (PIMAGEHLP_SYMBOL64) malloc( sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMNAME_SIZE );
  4796. if (Symbol == NULL){
  4797. FPRINTF(stderr, "KERNRATE: Allocation for Symbol in PrivEnumerateSymbols failed\n");
  4798. exit(1);
  4799. }
  4800. Symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
  4801. Symbol->MaxNameLength = MAX_SYMNAME_SIZE;
  4802. CurrentSym[0] = '\0';
  4803. Base = (BaseOptional == 0)? Current->Base : BaseOptional;
  4804. CurrentAddr = Base;
  4805. Length = (SizeOptional == 0)? Current->Length : SizeOptional;
  4806. TopAddress = (SizeOptional == 0)? Current->Base + Current->Length : BaseOptional + SizeOptional;
  4807. if (CurrentAddr == 0) {
  4808. FPRINTF(stderr, "KERNRATE: Zero base address passed to PrivEnumerateSymbols for module %s\n",
  4809. Current->module_Name
  4810. );
  4811. free(Symbol);
  4812. Symbol = NULL;
  4813. return (FALSE);
  4814. }
  4815. if (Length == 0) {
  4816. FPRINTF(stderr, "KERNRATE: Zero module length passed to PrivEnumerateSymbols for module %s\n",
  4817. Current->module_Name
  4818. );
  4819. free(Symbol);
  4820. Symbol = NULL;
  4821. return (FALSE);
  4822. }
  4823. //
  4824. // find first symbol
  4825. //
  4826. for (i=0; i < Length; i++){
  4827. j = i;
  4828. if ( (SymGetSymFromAddr64( SymHandle, CurrentAddr, &Displacement, Symbol ))) {
  4829. if( 0 == strcmp(Symbol->Name, "\0") )
  4830. continue;
  4831. strncpy(CurrentSym, Symbol->Name, cMODULE_NAME_STRLEN-1);
  4832. CurrentSym[cMODULE_NAME_STRLEN-1] = '\0';
  4833. bFoundSym = TRUE;
  4834. break;
  4835. }
  4836. ++CurrentAddr;
  4837. }
  4838. if( !bFoundSym ){
  4839. free(Symbol);
  4840. Symbol = NULL;
  4841. return (FALSE);
  4842. }
  4843. step = (INITIAL_STEP < Length)?INITIAL_STEP : 1;
  4844. Size = 1;
  4845. if( gVerbose & VERBOSE_INTERNALS )
  4846. FPRINTF(stdout, "\nPrivEnumSymbols Verbose Detail: Symbol, Address Range, Size\n");
  4847. for (i=j+step; i < Length; ){
  4848. k = i;
  4849. if ( (!SymGetSymFromAddr64( SymHandle, Base + i, &Displacement, Symbol ))) {
  4850. strncpy(Symbol->Name, "NO_SYMBOL", cMODULE_NAME_STRLEN-1);
  4851. Symbol->Name[cMODULE_NAME_STRLEN-1] = '\0';
  4852. }
  4853. if( !strcmp(CurrentSym, Symbol->Name) ) { //Same symbol, increase size and continue stepping
  4854. Size += step;
  4855. i = k + step;
  4856. if(i < Length)
  4857. continue;
  4858. } else { // not same symbol, decrease step and go back to find boudary
  4859. step >>=1;
  4860. if (step > 0){
  4861. i = k - step; // k was incremented by previous step so it's larger than present step
  4862. continue;
  4863. }
  4864. }
  4865. if( 0 != strcmp(Symbol->Name, "NO_SYMBOL") && 0 != strcmp(Symbol->Name, "\0") ){
  4866. if ( !CreateZoomModuleCallback(CurrentSym, CurrentAddr, Size, pProc) ) {
  4867. FPRINTF(stderr, "KERNRATE: Failed CallBack in PrivEnumerateSymbols Address= %I64x for module %s\n",
  4868. CurrentAddr,
  4869. Current->module_Name
  4870. );
  4871. free(Symbol);
  4872. Symbol = NULL;
  4873. return (FALSE);
  4874. }
  4875. }
  4876. if( gVerbose & VERBOSE_INTERNALS )
  4877. FPRINTF(stdout, "%s, 0x%p - 0x%p, 0x%lx\n", CurrentSym, (PVOID)CurrentAddr, (PVOID)(CurrentAddr+Size), Size);
  4878. strncpy(CurrentSym, Symbol->Name, cMODULE_NAME_STRLEN-1); //Put the next symbol into current symbol
  4879. CurrentSym[cMODULE_NAME_STRLEN-1] = '\0';
  4880. CurrentAddr += Size; //Advance to next method base
  4881. Size = 1; //Reset initial size and step for the next symbol
  4882. if ( INITIAL_STEP < (TopAddress - CurrentAddr) ){
  4883. step = INITIAL_STEP;
  4884. } else {
  4885. step = 1;
  4886. }
  4887. i = k + step;
  4888. }//for i
  4889. if (Symbol != NULL) {
  4890. free(Symbol);
  4891. Symbol = NULL;
  4892. }
  4893. return TRUE;
  4894. } //PrivEnumerateSymbols()
  4895. BOOL
  4896. HitsFound(
  4897. IN PPROC_TO_MONITOR pProc,
  4898. IN ULONG BucketIndex
  4899. )
  4900. /*++
  4901. Routine Description:
  4902. Determines if the current bucket scored any hits at all
  4903. Arguments:
  4904. pProc - Pointer to the structure of the process being monitored
  4905. BucketIndex - Index of the bucket matching the current address.
  4906. Return Value:
  4907. TRUE if hits are found, FALSE in case no hits are found
  4908. --*/
  4909. {
  4910. ULONG Index, CpuNumber;
  4911. for (Index=0; Index < gTotalActiveSources; Index++) {
  4912. for (CpuNumber=0; CpuNumber < (ULONG)gProfileProcessors; CpuNumber++) {
  4913. if ( 0 < gCallbackCurrent->Rate[gulActiveSources[Index]].ProfileBuffer[CpuNumber][BucketIndex] ){
  4914. return (TRUE);
  4915. }
  4916. }
  4917. }
  4918. return (FALSE);
  4919. }
  4920. VOID
  4921. CreateZoomedModuleList(
  4922. IN PMODULE ZoomModule,
  4923. IN ULONG RoundDown,
  4924. IN PPROC_TO_MONITOR pProc
  4925. )
  4926. /*++
  4927. Routine Description:
  4928. Creates a module list from the functions in a given module
  4929. Arguments:
  4930. ZoomModule - Supplies the module whose zoomed module list is to be created
  4931. RoundDown - Used for selecting the method of symbol enumeration
  4932. ProcToMonitor - Pointer to the process to be monitored
  4933. Return Value:
  4934. Pointer to the zoomed module list
  4935. NULL on error.
  4936. --*/
  4937. {
  4938. BOOL Success = FALSE;
  4939. HANDLE SymHandle = pProc->ProcessHandle;
  4940. gCallbackCurrent = ZoomModule;
  4941. //MC
  4942. if (( bMCHelperLoaded == TRUE ) && (!_stricmp(ZoomModule->module_FullName, "JIT_TYPE"))){
  4943. pfnAttachToProcess((DWORD)pProc->Pid);
  4944. Success = EnumerateSymbolsByBuckets( NULL,
  4945. ZoomModule,
  4946. NULL,
  4947. pProc
  4948. );
  4949. pfnDetachFromProcess();
  4950. //MC
  4951. } else {
  4952. if (RoundDown == 0) {
  4953. Success = EnumerateSymbolsByBuckets( SymHandle,
  4954. ZoomModule,
  4955. CreateZoomModuleCallback,
  4956. pProc
  4957. );
  4958. //MC
  4959. //
  4960. // If we failed the imagehlp call we have to check if this is a pre-compiled JIT module (ngen)
  4961. // We cannot count on the imagehlp return value because it will return success even if no symbols are found
  4962. //
  4963. if ( (bImageHlpSymbolFound == FALSE) && ( bMCHelperLoaded == TRUE ) ){
  4964. pfnAttachToProcess((DWORD)pProc->Pid);
  4965. Success = EnumerateSymbolsByBuckets( NULL,
  4966. ZoomModule,
  4967. NULL,
  4968. pProc
  4969. );
  4970. pfnDetachFromProcess();
  4971. }
  4972. //MC
  4973. } else {
  4974. Success = TkEnumerateSymbols( SymHandle,
  4975. ZoomModule,
  4976. CreateZoomModuleCallback,
  4977. pProc
  4978. );
  4979. }
  4980. }
  4981. if (!Success) {
  4982. DWORD ErrorCode = GetLastError();
  4983. FPRINTF(stderr,
  4984. "Symbol Enumeration failed (or no symbols found) on module %s in CreateZoomedModuleList, Error Code= %x\n",
  4985. ZoomModule->module_Name,
  4986. ErrorCode
  4987. );
  4988. }
  4989. //MC
  4990. bImageHlpSymbolFound = FALSE; //Reset for next module
  4991. //MC
  4992. return;
  4993. } // CreateZoomedModuleList()
  4994. VOID
  4995. OutputModuleList(
  4996. IN FILE *Out,
  4997. IN PMODULE ModuleList,
  4998. IN ULONG NumberModules,
  4999. IN PPROC_TO_MONITOR ProcToMonitor,
  5000. IN PMODULE Parent
  5001. )
  5002. /*++
  5003. Routine Description:
  5004. Outputs the given module list
  5005. Arguments:
  5006. Out - Supplies the FILE * where the output should go.
  5007. ModuleList - Supplies the list of modules to output
  5008. NumberModules - Supplies the number of modules in the list
  5009. ProcToMonitor - Pointer to the process to be monitored
  5010. Return Value:
  5011. None.
  5012. --*/
  5013. {
  5014. PRATE_DATA RateData;
  5015. PRATE_DATA SummaryData;
  5016. PRATE_SUMMARY RateSummary;
  5017. BOOL Header;
  5018. ULONG i, j, ProfileSourceIndex, Index;
  5019. PMODULE *ModuleArray;
  5020. PMODULE Current, tmpModule;
  5021. LONG CpuNumber;
  5022. ULONGLONG TempTotalCount, TempDoubtfulCount;
  5023. //// Beginning of Function Assertions Section:
  5024. //
  5025. //
  5026. //
  5027. // It is not really a bug but we are printing only the first 132 characters of the module name.
  5028. // This assertion will remind us this.
  5029. //
  5030. assert( sizeof(Current->module_Name) >= cMODULE_NAME_STRLEN );
  5031. //
  5032. //
  5033. //// End of Function Assertions Section
  5034. RateSummary = calloc(gSourceMaximum, sizeof (RATE_SUMMARY));
  5035. if (RateSummary == NULL) {
  5036. FPRINTF(stderr, "KERNRATE: Buffer allocation failed(1) while doing output of Module list\n");
  5037. exit(1);
  5038. }
  5039. SummaryData = calloc(gSourceMaximum, (RATE_DATA_FIXED_SIZE + RATE_DATA_VAR_SIZE));
  5040. if (SummaryData == NULL) {
  5041. FPRINTF(stderr, "KERNRATE: Buffer allocation failed(2) while doing output of Module list\n");
  5042. free(RateSummary);
  5043. RateSummary = NULL;
  5044. exit(1);
  5045. }
  5046. for (Index=0; Index < gTotalActiveSources; Index++) {
  5047. ProfileSourceIndex = gulActiveSources[Index];
  5048. SummaryData[ProfileSourceIndex].Rate = 0;
  5049. //
  5050. // Walk through the module list and compute the summary
  5051. // and collect the interesting per-module data.
  5052. //
  5053. RateSummary[ProfileSourceIndex].TotalCount = 0;
  5054. RateSummary[ProfileSourceIndex].Modules = malloc(NumberModules*sizeof(PMODULE));
  5055. if (RateSummary[ProfileSourceIndex].Modules == NULL) {
  5056. FPRINTF(stderr, "KERNRATE: Buffer allocation failed(3) while doing output of Module list\n");
  5057. exit(1);
  5058. }
  5059. RateSummary[ProfileSourceIndex].ModuleCount = 0;
  5060. ModuleArray = RateSummary[ProfileSourceIndex].Modules;
  5061. Current = ModuleList;
  5062. while (Current != NULL) {
  5063. RateData = &Current->Rate[ProfileSourceIndex];
  5064. TempTotalCount = 0;
  5065. TempDoubtfulCount = 0;
  5066. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  5067. TempTotalCount += RateData->TotalCount[CpuNumber];
  5068. }
  5069. TempDoubtfulCount = RateData->DoubtfulCounts;
  5070. RateData->GrandTotalCount = TempTotalCount;
  5071. if (TempTotalCount > 0) {
  5072. RateSummary[ProfileSourceIndex].TotalCount += TempTotalCount;
  5073. //
  5074. // Find if we already have a module by that name (optimizations may have split the module to several pieces)
  5075. // This will slow down processing, so we'll turn it off if the user decided to use the '-e' option to haste
  5076. // the output (in cases that monitored processes might go away frequently for example, we want to finish symbol
  5077. // processing before the process goes away, so any extra processing delay should be removed).
  5078. //
  5079. if(bIncludeGeneralInfo == TRUE) {
  5080. for ( i=0; i < RateSummary[ProfileSourceIndex].ModuleCount; i++){
  5081. if ( !strcmp(Current->module_Name, ModuleArray[i]->module_Name) ){ //Found a match
  5082. if (gVerbose & VERBOSE_INTERNALS)
  5083. FPRINTF(stdout, "===> Found module %s more than once, merged hit counts and re-sorted\n",
  5084. Current->module_Name
  5085. );
  5086. ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount += TempTotalCount; // Update the original module
  5087. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  5088. ModuleArray[i]->Rate[ProfileSourceIndex].TotalCount[CpuNumber] += RateData->TotalCount[CpuNumber];
  5089. }
  5090. ModuleArray[i]->Rate[ProfileSourceIndex].DoubtfulCounts += TempDoubtfulCount; // Update the shared counts total
  5091. //
  5092. // Re-Sort since number of hits changed
  5093. //
  5094. for (j=0; j<RateSummary[ProfileSourceIndex].ModuleCount; j++) {
  5095. if ( i > j && ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount > ModuleArray[j]->Rate[ProfileSourceIndex].GrandTotalCount) {
  5096. //
  5097. // insert here
  5098. //
  5099. tmpModule = ModuleArray[i]; //preserve the ptr to current module
  5100. MoveMemory(&ModuleArray[j+1], //shift the ptr array by one index to free the array element at j
  5101. &ModuleArray[j],
  5102. sizeof(PMODULE)*(i-j)
  5103. );
  5104. ModuleArray[j] = tmpModule; //set the free array element to the preserved ptr
  5105. break;
  5106. }
  5107. }
  5108. goto NEXT_1; //Go to the next module on the list (skipping current as a new module)
  5109. }
  5110. }
  5111. }
  5112. //
  5113. // No match found, so insert the new module in a sorted position in the array.
  5114. //
  5115. ModuleArray[RateSummary[ProfileSourceIndex].ModuleCount] = Current;
  5116. RateSummary[ProfileSourceIndex].ModuleCount++;
  5117. if (RateSummary[ProfileSourceIndex].ModuleCount > NumberModules) {
  5118. DbgPrint("Error, ModuleCount %d > NumberModules %d for Source %s\n",
  5119. RateSummary[ProfileSourceIndex].ModuleCount,
  5120. NumberModules,
  5121. ProcToMonitor->Source[ProfileSourceIndex].Name
  5122. );
  5123. DbgBreakPoint();
  5124. }
  5125. for (i=0; i<RateSummary[ProfileSourceIndex].ModuleCount; i++) {
  5126. if (TempTotalCount > ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount) {
  5127. //
  5128. // insert here
  5129. //
  5130. MoveMemory(&ModuleArray[i+1],
  5131. &ModuleArray[i],
  5132. sizeof(PMODULE)*(RateSummary[ProfileSourceIndex].ModuleCount-i-1)
  5133. );
  5134. ModuleArray[i] = Current;
  5135. break;
  5136. }
  5137. }
  5138. }
  5139. NEXT_1: Current = Current->Next;
  5140. }
  5141. if (RateSummary[ProfileSourceIndex].TotalCount > (ULONGLONG)0 ) {
  5142. //
  5143. // Output the result
  5144. //
  5145. PSOURCE s;
  5146. s = &ProcToMonitor->Source[ProfileSourceIndex];
  5147. if(Parent == NULL){
  5148. FPRINTF(Out, "\n%s %I64u hits, %ld events per hit --------\n",
  5149. s->Name,
  5150. RateSummary[ProfileSourceIndex].TotalCount,
  5151. s->Interval
  5152. );
  5153. } else {
  5154. FPRINTF(Out, "\n%s %I64u hits, %ld events per hit --------",
  5155. s->Name,
  5156. Parent->Rate[ProfileSourceIndex].GrandTotalCount,
  5157. s->Interval
  5158. );
  5159. if( gVerbose & VERBOSE_PROFILING ) {
  5160. FPRINTF(Out, " (%I64u total hits from summing-up the module components)\n",
  5161. RateSummary[ProfileSourceIndex].TotalCount
  5162. );
  5163. } else {
  5164. FPRINTF(Out, "\n");
  5165. }
  5166. }
  5167. if ( gVerbose & VERBOSE_PROFILING ) {
  5168. FPRINTF(Out," Module Hits Shared msec %%Total %%Certain Events/Sec\n");
  5169. } else {
  5170. FPRINTF(Out," Module Hits msec %%Total Events/Sec\n");
  5171. }
  5172. for (i=0; i < RateSummary[ProfileSourceIndex].ModuleCount; i++) {
  5173. Current = ModuleArray[i];
  5174. if ( ModuleArray[i]->Rate[ProfileSourceIndex].GrandTotalCount >= (ULONGLONG)gMinHitsToDisplay ) {
  5175. FPRINTF(Out, "%-32s", Current->module_Name); // Note only the first 132 characters are printed.
  5176. OutputLine(Out,
  5177. ProfileSourceIndex,
  5178. Current,
  5179. &RateSummary[ProfileSourceIndex],
  5180. ProcToMonitor
  5181. );
  5182. }
  5183. SummaryData[ProfileSourceIndex].Rate += Current->Rate[ProfileSourceIndex].Rate;
  5184. SummaryData[ProfileSourceIndex].GrandTotalCount += Current->Rate[ProfileSourceIndex].GrandTotalCount;
  5185. }
  5186. } else {
  5187. FPRINTF(Out, "\n%s - No Hits Recorded\n", ProcToMonitor->Source[ProfileSourceIndex].Name );
  5188. }
  5189. FPRINTF(Out, "\n");
  5190. }
  5191. //
  5192. // Output interesting data for the summary.
  5193. //
  5194. if( bGetInterestingData == TRUE ) {
  5195. FPRINTF(stdout,
  5196. "\n-------------- INTERESTING SUMMARY DATA ----------------------\n"
  5197. );
  5198. OutputInterestingData(Out, SummaryData);
  5199. }
  5200. //
  5201. // Output the results ordered by module
  5202. //
  5203. Current = ModuleList;
  5204. while (Current != NULL) {
  5205. Header = FALSE;
  5206. if ( gVerbose & VERBOSE_MODULES ) { //The printout below duplicates data already printed, let's limit the flood
  5207. for (Index=0; Index < gTotalActiveSources; Index++) {
  5208. ProfileSourceIndex = gulActiveSources[Index];
  5209. if ( Current->Rate[ProfileSourceIndex].GrandTotalCount > 0 ) {
  5210. if (!Header) {
  5211. FPRINTF(Out,"\nMODULE %s --------\n",Current->module_Name);
  5212. if ( gVerbose & VERBOSE_PROFILING ) {
  5213. FPRINTF(Out," %-*s Hits Shared msec %%Total %%Certain Events/Sec\n", gDescriptionMaxLen, "Source");
  5214. } else {
  5215. FPRINTF(Out," %-*s Hits msec %%Total Events/Sec\n", gDescriptionMaxLen, "Source");
  5216. }
  5217. Header = TRUE;
  5218. }
  5219. FPRINTF(Out, "%-*s", gDescriptionMaxLen, ProcToMonitor->Source[ProfileSourceIndex].Name);
  5220. OutputLine(Out,
  5221. ProfileSourceIndex,
  5222. Current,
  5223. &RateSummary[ProfileSourceIndex],
  5224. ProcToMonitor);
  5225. }
  5226. }
  5227. }
  5228. //
  5229. // Output interesting data for the module.
  5230. //
  5231. if( bGetInterestingData == TRUE ) {
  5232. FPRINTF(stdout,
  5233. "\n-------------- INTERESTING MODULE DATA FOR %s---------------------- \n",
  5234. Current->module_Name
  5235. );
  5236. OutputInterestingData(Out, &Current->Rate[0]);
  5237. }
  5238. Current = Current->Next;
  5239. }
  5240. return;
  5241. } // OutputModuleList()
  5242. VOID
  5243. OutputLine(
  5244. IN FILE *Out,
  5245. IN ULONG ProfileSourceIndex,
  5246. IN PMODULE Module,
  5247. IN PRATE_SUMMARY RateSummary,
  5248. IN PPROC_TO_MONITOR ProcToMonitor
  5249. )
  5250. /*++
  5251. Routine Description:
  5252. Outputs a line corresponding to the particular module/source
  5253. Arguments:
  5254. Out - Supplies the file pointer to output to.
  5255. ProfileSource - Supplies the source to use
  5256. Module - Supplies the module to be output
  5257. RateSummary - Supplies the rate summary for this source
  5258. ProcToMonitor - Pointer to the process to be monitored
  5259. Return Value:
  5260. None.
  5261. --*/
  5262. {
  5263. ULONG Msec;
  5264. ULONGLONG Events;
  5265. ULONGLONG TempTotalCount;
  5266. PRATE_DATA RateData;
  5267. LONG CpuNumber = 0;
  5268. RateData = &Module->Rate[ProfileSourceIndex];
  5269. TempTotalCount = RateData->GrandTotalCount;
  5270. //
  5271. //The time is in 100ns units = 0.1us = 1/10,000ms
  5272. //The events are fired every 100ns=0.1us=1/10,000ms (or 10,000,000 events per second)
  5273. //
  5274. Msec = (ULONG)(RateData->TotalTime/10000);
  5275. Events = TempTotalCount * ProcToMonitor->Source[ProfileSourceIndex].Interval * 1000; //To get Events/sec below
  5276. if ( gVerbose & VERBOSE_PROFILING ) {
  5277. FPRINTF(Out,
  5278. " %10I64u %10I64u %10ld %2d %% %2d %% ",
  5279. TempTotalCount,
  5280. RateData->DoubtfulCounts,
  5281. Msec,
  5282. (ULONG)(100*TempTotalCount/
  5283. RateSummary->TotalCount),
  5284. (ULONG)(100*(TempTotalCount - RateData->DoubtfulCounts)/
  5285. RateSummary->TotalCount)
  5286. );
  5287. } else {
  5288. FPRINTF(Out,
  5289. " %10I64u %10ld %2d %% ",
  5290. TempTotalCount,
  5291. Msec,
  5292. (ULONG)(100*TempTotalCount/
  5293. RateSummary->TotalCount)
  5294. );
  5295. }
  5296. if (Msec > 0) {
  5297. RateData->Rate = Events/Msec; //The final result is in Events/sec
  5298. FPRINTF(Out, "%10I64u\n", RateData->Rate);
  5299. } else {
  5300. RateData->Rate = 0;
  5301. FPRINTF(Out,"---\n");
  5302. }
  5303. if (bProfileByProcessor) {
  5304. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  5305. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  5306. TempTotalCount = RateData->TotalCount[CpuNumber];
  5307. Events = TempTotalCount * ProcToMonitor->Source[ProfileSourceIndex].Interval * 1000;
  5308. FPRINTF(Out,
  5309. "%6d %7I64u %6ld %2d %% ",
  5310. CpuNumber,
  5311. TempTotalCount,
  5312. Msec,
  5313. (ULONG)(100*TempTotalCount/RateSummary->TotalCount));
  5314. if (Msec > 0) {
  5315. FPRINTF(Out,"%10I64d\n", Events/Msec); //The final result is in Events/sec
  5316. } else {
  5317. FPRINTF(Out,"---\n");
  5318. }
  5319. }
  5320. }
  5321. } //OutputLine()
  5322. VOID
  5323. OutputInterestingData(
  5324. IN FILE *Out,
  5325. IN RATE_DATA Data[]
  5326. )
  5327. /*++
  5328. Routine Description:
  5329. Computes interesting Processor Statistics and outputs them.
  5330. Arguments:
  5331. Out - Supplies the file pointer to output to.
  5332. Data - Supplies an array of RATE_DATA. The Rate field is the only interesting part.
  5333. Header - Supplies header to be printed.
  5334. Return Value:
  5335. None.
  5336. --*/
  5337. {
  5338. ULONGLONG Temp1,Temp2;
  5339. LONGLONG Temp3;
  5340. float Ratio;
  5341. BOOL DataFound = FALSE;
  5342. //
  5343. // Note that we have to do a lot of funky (float)(LONGLONG) casts in order
  5344. // to prevent the weenie x86 compiler from choking.
  5345. //
  5346. //
  5347. // Compute cycles/instruction and instruction mix data.
  5348. //
  5349. if ((Data[ProfileTotalIssues].Rate > 0) &&
  5350. (Data[ProfileTotalIssues].GrandTotalCount > 10)) {
  5351. if (Data[ProfileTotalCycles].Rate > 0) {
  5352. Ratio = (float)(LONGLONG)(Data[ProfileTotalCycles].Rate)/
  5353. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5354. DataFound = TRUE;
  5355. FPRINTF(Out, "Cycles per instruction\t\t%6.2f\n", Ratio);
  5356. }
  5357. if (Data[ProfileLoadInstructions].Rate > 0) {
  5358. Ratio = (float)(LONGLONG)(Data[ProfileLoadInstructions].Rate)/
  5359. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5360. if (Ratio >= 0.01 && Ratio <= 1.0) {
  5361. DataFound = TRUE;
  5362. FPRINTF(Out, "Load instruction percentage\t%6.2f %%\n",Ratio*100);
  5363. }
  5364. }
  5365. if (Data[ProfileStoreInstructions].Rate > 0) {
  5366. Ratio = (float)(LONGLONG)(Data[ProfileStoreInstructions].Rate)/
  5367. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5368. if (Ratio >= 0.01 && Ratio <= 1.0) {
  5369. DataFound = TRUE;
  5370. FPRINTF(Out, "Store instruction percentage\t%6.2f %%\n",Ratio*100);
  5371. }
  5372. }
  5373. if (Data[ProfileBranchInstructions].Rate > 0) {
  5374. Ratio = (float)(LONGLONG)(Data[ProfileBranchInstructions].Rate)/
  5375. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5376. if (Ratio >= 0.01 && Ratio <= 1.0) {
  5377. DataFound = TRUE;
  5378. FPRINTF(Out, "Branch instruction percentage\t%6.2f %%\n",Ratio*100);
  5379. }
  5380. }
  5381. if (Data[ProfileFpInstructions].Rate > 0) {
  5382. Ratio = (float)(LONGLONG)(Data[ProfileFpInstructions].Rate)/
  5383. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5384. if (Ratio >= 0.01 && Ratio <= 1.0) {
  5385. DataFound = TRUE;
  5386. FPRINTF(Out, "FP instruction percentage\t%6.2f %%\n",Ratio*100);
  5387. }
  5388. }
  5389. if (Data[ProfileIntegerInstructions].Rate > 0) {
  5390. Ratio = (float)(LONGLONG)(Data[ProfileIntegerInstructions].Rate)/
  5391. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5392. if (Ratio >= 0.01 && Ratio <= 1.0) {
  5393. DataFound = TRUE;
  5394. FPRINTF(Out, "Integer instruction percentage\t%6.2f %%\n",Ratio*100);
  5395. }
  5396. }
  5397. //
  5398. // Compute icache hit rate
  5399. //
  5400. if (Data[ProfileIcacheMisses].Rate > 0) {
  5401. Temp3 = (LONGLONG)(Data[ProfileTotalIssues].Rate - Data[ProfileIcacheMisses].Rate);
  5402. if(Temp3 > 0){
  5403. Ratio = (float)Temp3/
  5404. (float)(LONGLONG)(Data[ProfileTotalIssues].Rate);
  5405. if( Ratio <= 1.0 ) {
  5406. DataFound = TRUE;
  5407. FPRINTF(Out, "Icache hit rate\t\t\t%6.2f %%\n", Ratio*100);
  5408. }
  5409. }
  5410. }
  5411. }
  5412. //
  5413. // Compute dcache hit rate
  5414. //
  5415. if( Data[ProfileLoadInstructions].Rate > 0 && Data[ProfileStoreInstructions].Rate > 0 ){
  5416. Temp1 = Data[ProfileLoadInstructions].Rate + Data[ProfileStoreInstructions].Rate;
  5417. if ((Data[ProfileDcacheMisses].Rate > 0) &&
  5418. (Temp1 != 0) &&
  5419. (Data[ProfileDcacheMisses].GrandTotalCount > 10)) {
  5420. Temp2 = Temp1 - Data[ProfileDcacheMisses].Rate;
  5421. Temp3 = (LONGLONG) Temp2;
  5422. Ratio = (float)Temp3/(float)(LONGLONG)Temp1;
  5423. if( Temp3 > 0 && Ratio <= 1.0 ) {
  5424. DataFound = TRUE;
  5425. FPRINTF(Out, "Dcache hit rate\t\t\t%6.2f %%\n", Ratio*100);
  5426. }
  5427. }
  5428. }
  5429. //
  5430. // Compute branch prediction hit percentage
  5431. //
  5432. if ((Data[ProfileBranchInstructions].Rate > 0) &&
  5433. (Data[ProfileBranchMispredictions].Rate > 0) &&
  5434. (Data[ProfileBranchInstructions].GrandTotalCount > 10)) {
  5435. Temp3 = (LONGLONG)(Data[ProfileBranchInstructions].Rate-Data[ProfileBranchMispredictions].Rate);
  5436. if(Temp3 > 0){
  5437. Ratio = (float)Temp3 /
  5438. (float)(LONGLONG)(Data[ProfileBranchInstructions].Rate);
  5439. if( Ratio <= 1.0 ) {
  5440. DataFound = TRUE;
  5441. FPRINTF(Out, "Branch predict hit percentage\t%6.2f %%\n", Ratio*100);
  5442. }
  5443. }
  5444. }
  5445. if ( !DataFound )
  5446. FPRINTF(Out, "===> No interesting data found or hit counts too low\n");
  5447. } // OutputInterestingData()
  5448. /* BEGIN_IMS CreateNewModule
  5449. ******************************************************************************
  5450. ****
  5451. **** CreateNewModule ( )
  5452. ****
  5453. ******************************************************************************
  5454. *
  5455. * Function Description:
  5456. *
  5457. * This function allocates and initializes a module entry.
  5458. *
  5459. * Arguments:
  5460. *
  5461. * IN HANDLE ProcessHandle :
  5462. *
  5463. * IN PCHAR ModuleName :
  5464. *
  5465. * IN PCHAR ModuleFullName :
  5466. *
  5467. * IN ULONG ImageBase :
  5468. *
  5469. * IN ULONG ImageSize :
  5470. *
  5471. * Return Value:
  5472. *
  5473. * PMODULE
  5474. *
  5475. * Algorithm:
  5476. *
  5477. * ToBeSpecified
  5478. *
  5479. * Globals Referenced:
  5480. *
  5481. * ToBeSpecified
  5482. *
  5483. * Exception Conditions:
  5484. *
  5485. * ToBeSpecified
  5486. *
  5487. * In/Out Conditions:
  5488. *
  5489. * ToBeSpecified
  5490. *
  5491. * Notes:
  5492. *
  5493. * ToBeSpecified
  5494. *
  5495. * ToDo List:
  5496. *
  5497. * ToBeSpecified
  5498. *
  5499. * Modification History:
  5500. *
  5501. * 9/8/97 TF Initial version
  5502. *
  5503. ******************************************************************************
  5504. * END_IMS CreateNewModule */
  5505. PMODULE
  5506. CreateNewModule(
  5507. IN PPROC_TO_MONITOR ProcToMonitor,
  5508. IN PCHAR ModuleName,
  5509. IN PCHAR ModuleFullName,
  5510. IN ULONG64 ImageBase,
  5511. IN ULONG ImageSize
  5512. )
  5513. {
  5514. PMODULE NewModule;
  5515. PMODULE ZoomModule;
  5516. HANDLE ProcessHandle = ProcToMonitor->ProcessHandle;
  5517. PCHAR lastptr = NULL, dotptr = NULL;
  5518. NewModule = calloc(1, MODULE_SIZE);
  5519. if (NewModule == NULL) {
  5520. FPRINTF(stderr, "Memory allocation of NewModule for %s failed\n", ModuleName);
  5521. exit(1);
  5522. }
  5523. NewModule->bZoom = FALSE;
  5524. SetModuleName( NewModule, ModuleName );
  5525. //
  5526. // Following WinDbg's rule: module names are filenames without their extension.
  5527. // However, currently long file names may include more than one period
  5528. // We'll try to strip only the last extension and keep the rest
  5529. //
  5530. dotptr = strchr(NewModule->module_Name, '.');
  5531. while (dotptr != NULL){
  5532. lastptr = dotptr;
  5533. dotptr = strchr(dotptr+1, '.');
  5534. }
  5535. if(lastptr != NULL)
  5536. *lastptr = '\0';
  5537. //
  5538. // See if this module is on the zoom list.
  5539. //
  5540. ZoomModule = ProcToMonitor->ZoomList;
  5541. while ( ZoomModule != NULL ) {
  5542. //
  5543. // By default the user needs to specify only the module name (no extension) for a zoom module
  5544. // The 2nd part of the following check allows the user to specify a full file name for a zoom module
  5545. // This allows kernrate to make a distinction in cases that a .exe and a .dll for example carry
  5546. // the same module name
  5547. //
  5548. if ( _stricmp(ZoomModule->module_Name, NewModule->module_Name) == 0 ||
  5549. (NULL != ModuleName && 0 == _stricmp( ModuleName, ZoomModule->module_Name )) ) {
  5550. //
  5551. // found a match
  5552. //
  5553. NewModule->hProcess = ProcessHandle;
  5554. NewModule->Base = ImageBase;
  5555. NewModule->Length = ImageSize;
  5556. NewModule->bZoom = TRUE;
  5557. NewModule->module_FileName = _strdup( ModuleName ); // File name including extension
  5558. if ( ModuleFullName ) {
  5559. NewModule->module_FullName = _strdup( ModuleFullName ); // Including the fully qualified path
  5560. }
  5561. gCurrentModule = NewModule;
  5562. //
  5563. // Load symbols
  5564. //
  5565. // Note 15/09/97 TF: do not be confused here...
  5566. // In this routine, the ModuleName variable is a filename with its
  5567. // extension: File.exe or File.dll
  5568. //
  5569. // Note 30/09/97 TF: The current kernrate version does not change
  5570. // the default IMAGEHLP behaviour in terms of symbol file loading:
  5571. // It is synchronous ( and not deferred ) with the SymLoadModule
  5572. // call. Our registered callback will be called with the standard
  5573. // symbol file operations.
  5574. // If the kernrate behaviour changes, we will have to revisit this
  5575. // assumption.
  5576. //
  5577. //MC
  5578. if(0 != _stricmp(ModuleFullName, "JIT_TYPE")){
  5579. //MC
  5580. (void)SymLoadModule64( ProcessHandle, // hProcess
  5581. NULL, // hFile [for Debugger]
  5582. ModuleName, // ImageName
  5583. NULL, // ModuleName
  5584. ImageBase, // BaseOfDll
  5585. ImageSize // SizeOfDll
  5586. );
  5587. //MC
  5588. }
  5589. //MC
  5590. gCurrentModule = (PMODULE)0;
  5591. break;
  5592. }
  5593. ZoomModule = ZoomModule->Next;
  5594. } //while
  5595. if(NewModule->bZoom == FALSE){
  5596. NewModule->hProcess = ProcessHandle;
  5597. NewModule->Base = ImageBase; // Note TF: I know for zoomed it is a redone...
  5598. NewModule->Length = ImageSize; // Note TF: I know for zoomed it is a redone...
  5599. assert( ModuleName );
  5600. if ( NewModule->module_FileName == (PCHAR)0 ) {
  5601. NewModule->module_FileName = _strdup( ModuleName );
  5602. }
  5603. if ( ModuleFullName && NewModule->module_FullName == (PCHAR)0 ) {
  5604. NewModule->module_FullName = _strdup( ModuleFullName );
  5605. }
  5606. }
  5607. #define VerboseModuleFormat "0x%p 0x%p "
  5608. VerbosePrint(( VERBOSE_MODULES, VerboseModuleFormat " %s [%s]\n",
  5609. (PVOID)NewModule->Base,
  5610. (PVOID)(NewModule->Base + (ULONG64)NewModule->Length),
  5611. NewModule->module_Name,
  5612. ModuleFullName
  5613. ));
  5614. #undef VerboseModuleFormat
  5615. return(NewModule);
  5616. } // CreateNewModule()
  5617. BOOL
  5618. InitializeAsDebugger(VOID)
  5619. {
  5620. HANDLE Token;
  5621. PTOKEN_PRIVILEGES NewPrivileges;
  5622. LUID LuidPrivilege;
  5623. BOOL bRet = FALSE;
  5624. //
  5625. // Make sure we have access to adjust and to get the old token privileges
  5626. //
  5627. if (!OpenProcessToken( GetCurrentProcess(),
  5628. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  5629. &Token)) {
  5630. return( FALSE );
  5631. }
  5632. //
  5633. // Initialize the privilege adjustment structure
  5634. //
  5635. LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidPrivilege );
  5636. NewPrivileges = (PTOKEN_PRIVILEGES)calloc(1,sizeof(TOKEN_PRIVILEGES) +
  5637. (1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
  5638. if (NewPrivileges == NULL) {
  5639. CloseHandle(Token);
  5640. return( FALSE );
  5641. }
  5642. NewPrivileges->PrivilegeCount = 1;
  5643. NewPrivileges->Privileges[0].Luid = LuidPrivilege;
  5644. NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  5645. //
  5646. // Enable the privilege
  5647. //
  5648. bRet = AdjustTokenPrivileges( Token,
  5649. FALSE,
  5650. NewPrivileges,
  5651. 0,
  5652. (PTOKEN_PRIVILEGES)NULL,
  5653. NULL );
  5654. CloseHandle( Token );
  5655. free( NewPrivileges );
  5656. return (bRet);
  5657. } // InitializeAsDebugger()
  5658. VOID
  5659. InitSymbolPath(
  5660. HANDLE SymHandle
  5661. )
  5662. {
  5663. PCHAR tmpPath;
  5664. LONG CharsLeft;
  5665. CHAR WinDirPath[] = ";%Windir%\\System32\\Drivers;%Windir%\\System32;%Windir%";
  5666. CHAR DriversPath[] = "\\system32\\drivers;";
  5667. CHAR System32Path[] = "\\system32;";
  5668. CHAR PathSeparator[] = ";";
  5669. tmpPath = malloc(TOTAL_SYMPATH_LENGTH*sizeof(char));
  5670. if(tmpPath == NULL){
  5671. FPRINTF(stderr, "KERNRATE: Failed memory allocation for tmpPath in InitSymbolPath\n");
  5672. exit(1);
  5673. }
  5674. if ( SymSetSearchPath(SymHandle, (LPSTR)0 ) == TRUE ) {
  5675. // When SymSetSearchPath() is called with SearchPath as NULL, the following
  5676. // symbol path default is used:
  5677. // .;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;
  5678. if ( gUserSymbolPath[0] != '\0' ) {
  5679. //
  5680. // Note: We prepend the user specified path to the current search path.
  5681. //
  5682. if ( SymGetSearchPath( SymHandle, tmpPath, sizeof( tmpPath ) ) == TRUE ) {
  5683. strncpy( gSymbolPath, gUserSymbolPath, USER_SYMPATH_LENGTH-1);
  5684. strncat( gSymbolPath, PathSeparator, lstrlen(PathSeparator) );
  5685. CharsLeft = TOTAL_SYMPATH_LENGTH - lstrlen(gSymbolPath)-1;
  5686. if ( (lstrlen(gSymbolPath) + lstrlen(tmpPath) ) > TOTAL_SYMPATH_LENGTH - 1 )
  5687. FPRINTF(stderr, "===>WARNING: Overall symbol path length exceeds %d characters and will be truncated\n",
  5688. TOTAL_SYMPATH_LENGTH
  5689. );
  5690. strncat( gSymbolPath, tmpPath, CharsLeft-1 );
  5691. gSymbolPath[ TOTAL_SYMPATH_LENGTH-1 ] = '\0';
  5692. if ( SymSetSearchPath( SymHandle, gSymbolPath ) != TRUE ) {
  5693. FPRINTF( stderr, "KERNRATE: Failed to set the user specified symbol search path.\nUsing default IMAGEHLP symbol search path...\n" );
  5694. }
  5695. }
  5696. }
  5697. //
  5698. // IMAGEHLP also looks for the executabe image. Let's append %windir%\system32\drivers;%windir%\system32;%windir%
  5699. // to the end of the path. This way privates will always be searched first in the current directory.
  5700. // Also, this order will allow to find executables in their "natural" directories first, before going to dllcache etc.
  5701. //
  5702. if ( SymGetSearchPath( SymHandle, gSymbolPath, sizeof( gSymbolPath ) ) == TRUE ) {
  5703. CharsLeft = TOTAL_SYMPATH_LENGTH - lstrlen( gSymbolPath ) - 1;
  5704. strncpy( tmpPath, WinDirPath, CharsLeft);
  5705. tmpPath[CharsLeft] = '\0';
  5706. if ( (lstrlen(gSymbolPath) + lstrlen(tmpPath)) > TOTAL_SYMPATH_LENGTH - 1 )
  5707. FPRINTF(stderr, "===>WARNING: Overall symbol path length exceeds %d characters and will be truncated\n",
  5708. TOTAL_SYMPATH_LENGTH
  5709. );
  5710. strncat( gSymbolPath, tmpPath, lstrlen(tmpPath) ); //tmpPath length is CharsLeft
  5711. gSymbolPath[ TOTAL_SYMPATH_LENGTH-1 ] = '\0';
  5712. if ( SymSetSearchPath(SymHandle, gSymbolPath) != TRUE ) {
  5713. FPRINTF( stderr, "KERNRATE: Failed to set the symbol search path with %%windir%%.\nCurrent symbol search path is: %s\n", gSymbolPath );
  5714. }
  5715. }
  5716. }
  5717. else {
  5718. FPRINTF( stderr, "KERNRATE: Failed to set the IMAGEHLP default symbol search path, trying to set to %%windir%% and sub directories\n" );
  5719. //
  5720. // Set the Symbol Search Path with "%WINDIR%" -
  5721. // it was the behaviour of the original MS code...
  5722. // Let's also append system32 and system32\drivers to the path so people will stop complaining
  5723. //
  5724. if( 0 != GetEnvironmentVariable("windir", gSymbolPath, sizeof(gSymbolPath)) ){
  5725. CharsLeft = TOTAL_SYMPATH_LENGTH - 1;
  5726. if( CharsLeft >= (lstrlen(System32Path) + 3*lstrlen(gSymbolPath) + lstrlen(DriversPath) + lstrlen(PathSeparator) ) ){
  5727. strncpy(tmpPath, gSymbolPath, TOTAL_SYMPATH_LENGTH - 1);
  5728. tmpPath[ TOTAL_SYMPATH_LENGTH-1 ] = '\0';
  5729. strncat(tmpPath, DriversPath, lstrlen(DriversPath) );
  5730. strncat(tmpPath, gSymbolPath, lstrlen(gSymbolPath) );
  5731. strncat(tmpPath, System32Path, lstrlen(System32Path) );
  5732. strncat(tmpPath, gSymbolPath, lstrlen(gSymbolPath) );
  5733. strncat(tmpPath, PathSeparator, lstrlen(PathSeparator) );
  5734. strncpy(gSymbolPath, tmpPath, TOTAL_SYMPATH_LENGTH - 1 );
  5735. gSymbolPath[TOTAL_SYMPATH_LENGTH - 1] = '\0';
  5736. }
  5737. else{
  5738. FPRINTF( stderr, "KERNRATE: Overall path length for %%windir%% and sub directories exceeds %d characters\n",
  5739. TOTAL_SYMPATH_LENGTH
  5740. );
  5741. }
  5742. SymSetSearchPath(SymHandle, gSymbolPath);
  5743. } else {
  5744. FPRINTF(stderr, "KERNRATE: Failed to get environment variable for %%windir%%, failed to set alternate symbol path\n");
  5745. }
  5746. }
  5747. //
  5748. // In any case [and it is redundant to do this in some of the previous cases],
  5749. // but we want to be in sync, especially for the image and debug files checksum check.
  5750. //
  5751. if ( SymGetSearchPath(SymHandle, gSymbolPath, sizeof( gSymbolPath ) ) != TRUE ) {
  5752. FPRINTF( stderr, "KERNRATE: Failed to get IMAGEHLP symbol files search path...\n" );
  5753. //
  5754. // The content of gSymbolPath is now undefined. so clean it...
  5755. // gSymbolPath[] users have to check the content.
  5756. //
  5757. gSymbolPath[0] = '\0';
  5758. }
  5759. else if ( gVerbose & VERBOSE_IMAGEHLP ) {
  5760. FPRINTF( stderr, "KERNRATE: IMAGEHLP symbol search path is: %s\n", gSymbolPath );
  5761. }
  5762. free( tmpPath );
  5763. bSymPathInitialized = TRUE;
  5764. } // InitSymbolPath()
  5765. BOOL
  5766. InitializeKernelProfile(VOID)
  5767. {
  5768. DWORD m,k;
  5769. PPROC_TO_MONITOR ProcToMonitor = calloc(1, sizeof(PROC_TO_MONITOR));
  5770. if (ProcToMonitor==NULL) {
  5771. FPRINTF(stderr, "Allocation for the System Process failed\n");
  5772. return(FALSE);
  5773. }
  5774. gpSysProc = ProcToMonitor;
  5775. ProcToMonitor->ProcessHandle = SYM_KERNEL_HANDLE;
  5776. ProcToMonitor->Index = gNumProcToMonitor;
  5777. ProcToMonitor->Next = gProcessList; // NULL
  5778. gProcessList = ProcToMonitor;
  5779. ProcToMonitor->ZoomList = gCommonZoomList; // gCommonZoomList may contain System modules
  5780. ProcToMonitor->pProcThreadInfoStart = NULL;
  5781. for(m=0; m<gNumTasksStart; m++){
  5782. if( !_stricmp(gTlistStart[m].ProcessName, gSystemProcessName) ){
  5783. ProcToMonitor->Pid = gTlistStart[m].ProcessId;
  5784. if( bSystemThreadsInfo == TRUE ){
  5785. UpdateProcessStartInfo(ProcToMonitor,
  5786. &gTlistStart[m],
  5787. bSystemThreadsInfo
  5788. );
  5789. }
  5790. break;
  5791. }
  5792. }
  5793. InitializeProfileSourceInfo(ProcToMonitor); // Initialize ProfileSourceInfo for kernel trace
  5794. // Update profiling rate for kernel trace if necessary
  5795. SetProfileSourcesRates(ProcToMonitor);
  5796. gNumProcToMonitor++;
  5797. if (!SymInitialize( SYM_KERNEL_HANDLE, NULL, FALSE )) {
  5798. FPRINTF (stderr, "Could not initialize imagehlp for kernel - %d\n", GetLastError ());
  5799. return (FALSE);
  5800. }
  5801. if (!bSymPathInitialized) {
  5802. InitSymbolPath( SYM_KERNEL_HANDLE );
  5803. }
  5804. else {
  5805. SymSetSearchPath(ProcToMonitor->ProcessHandle, gSymbolPath);
  5806. }
  5807. if ( SymRegisterCallback64( ProcToMonitor->ProcessHandle, SymbolCallbackFunction, (ULONG64)&gCurrentModule ) != TRUE ) {
  5808. FPRINTF( stderr, "KERNRATE: Failed to register callback for IMAGEHLP Kernel handle operations...\n" );
  5809. }
  5810. return TRUE;
  5811. } //InitializeKernelProfile()
  5812. DWORD
  5813. GetTaskList(
  5814. PTASK_LIST pTask,
  5815. ULONG NumTasks
  5816. )
  5817. /*++
  5818. Routine Description:
  5819. Provides an API for getting a list of tasks running at the time of the
  5820. API call. This function uses internal NT apis and data structures. This
  5821. api is MUCH faster that the non-internal version that uses the registry.
  5822. Arguments:
  5823. pTask - Pointer to a TASK_LIST struct
  5824. NumTasks - Maximum number of tasks that the pTask array can hold
  5825. Return Value:
  5826. Number of tasks placed into the pTask array.
  5827. --*/
  5828. {
  5829. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  5830. NTSTATUS status;
  5831. ANSI_STRING pname;
  5832. PCHAR p;
  5833. PUCHAR CommonLargeBuffer;
  5834. ULONG TotalOffset;
  5835. ULONG totalTasks = 0;
  5836. ULONG CommonLargeBufferSize = 64*1024;
  5837. do {
  5838. CommonLargeBuffer = VirtualAlloc (NULL,
  5839. CommonLargeBufferSize,
  5840. MEM_COMMIT,
  5841. PAGE_READWRITE);
  5842. if (CommonLargeBuffer == NULL) {
  5843. return 0;
  5844. }
  5845. status = NtQuerySystemInformation(
  5846. SystemProcessInformation,
  5847. CommonLargeBuffer,
  5848. CommonLargeBufferSize,
  5849. NULL
  5850. );
  5851. if (status == STATUS_INFO_LENGTH_MISMATCH) {
  5852. CommonLargeBufferSize += 8192;
  5853. VirtualFree (CommonLargeBuffer, 0, MEM_RELEASE);
  5854. CommonLargeBuffer = NULL;
  5855. }
  5856. else if ( !NT_SUCCESS(status) ) {
  5857. FPRINTF(stderr, "KERNRATE: NtQuerySystemInformation failed in getTaskList, status %08lx, aborting\n", status);
  5858. exit(1);
  5859. }
  5860. } while ( CommonLargeBuffer == NULL );
  5861. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) CommonLargeBuffer;
  5862. TotalOffset = 0;
  5863. do {
  5864. pname.Buffer = NULL;
  5865. if ( ProcessInfo->ImageName.Buffer ) {
  5866. status = RtlUnicodeStringToAnsiString( &pname, (PUNICODE_STRING)&ProcessInfo->ImageName, TRUE );
  5867. if ( NT_SUCCESS(status) && pname.Buffer ) {
  5868. p = strrchr(pname.Buffer,'\\');
  5869. if ( p ) {
  5870. p++;
  5871. }
  5872. else {
  5873. p = pname.Buffer;
  5874. }
  5875. }
  5876. else {
  5877. p = "???UToAStr err"; // RtlUnicodeStringToAnsiString error.
  5878. }
  5879. }
  5880. else {
  5881. p = "System Idle Process";
  5882. }
  5883. strncpy( pTask->ProcessName, p, sizeof(pTask->ProcessName)-1 );
  5884. pTask->ProcessId = (LONGLONG)ProcessInfo->UniqueProcessId;
  5885. if(bIncludeGeneralInfo){
  5886. pTask->ProcessPerfInfo.NumberOfThreads = ProcessInfo->NumberOfThreads;
  5887. pTask->ProcessPerfInfo.UserTime = ProcessInfo->UserTime;
  5888. pTask->ProcessPerfInfo.KernelTime = ProcessInfo->KernelTime;
  5889. pTask->ProcessPerfInfo.ReadOperationCount = ProcessInfo->ReadOperationCount;
  5890. pTask->ProcessPerfInfo.WriteOperationCount = ProcessInfo->WriteOperationCount;
  5891. pTask->ProcessPerfInfo.OtherOperationCount = ProcessInfo->OtherOperationCount;
  5892. pTask->ProcessPerfInfo.ReadTransferCount = ProcessInfo->ReadTransferCount;
  5893. pTask->ProcessPerfInfo.WriteTransferCount = ProcessInfo->WriteTransferCount;
  5894. pTask->ProcessPerfInfo.OtherTransferCount = ProcessInfo->OtherTransferCount;
  5895. pTask->ProcessPerfInfo.PageFaultCount = ProcessInfo->PageFaultCount;
  5896. pTask->ProcessPerfInfo.HandleCount = ProcessInfo->HandleCount;
  5897. pTask->ProcessPerfInfo.VirtualSize = ProcessInfo->VirtualSize;
  5898. pTask->ProcessPerfInfo.WorkingSetSize = ProcessInfo->WorkingSetSize;
  5899. pTask->ProcessPerfInfo.QuotaPagedPoolUsage = ProcessInfo->QuotaPagedPoolUsage;
  5900. pTask->ProcessPerfInfo.QuotaNonPagedPoolUsage = ProcessInfo->QuotaNonPagedPoolUsage;
  5901. pTask->ProcessPerfInfo.PagefileUsage = ProcessInfo->PagefileUsage;
  5902. pTask->ProcessPerfInfo.PrivatePageCount = ProcessInfo->PrivatePageCount;
  5903. if( bIncludeThreadsInfo == TRUE ){
  5904. if(pTask->pProcessThreadInfo != NULL) //This is not the first time we,ve been called
  5905. free(pTask->pProcessThreadInfo);
  5906. pTask->pProcessThreadInfo =
  5907. (PSYSTEM_THREAD_INFORMATION)calloc(ProcessInfo->NumberOfThreads,
  5908. sizeof(SYSTEM_THREAD_INFORMATION)
  5909. );
  5910. if (pTask->pProcessThreadInfo != NULL) {
  5911. UINT nThreads = ProcessInfo->NumberOfThreads;
  5912. PSYSTEM_THREAD_INFORMATION pSysThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
  5913. while (nThreads--){
  5914. pTask->pProcessThreadInfo[nThreads].KernelTime = pSysThreadInfo->KernelTime;
  5915. pTask->pProcessThreadInfo[nThreads].UserTime = pSysThreadInfo->UserTime;
  5916. pTask->pProcessThreadInfo[nThreads].CreateTime = pSysThreadInfo->CreateTime;
  5917. pTask->pProcessThreadInfo[nThreads].WaitTime = pSysThreadInfo->WaitTime;
  5918. pTask->pProcessThreadInfo[nThreads].StartAddress = pSysThreadInfo->StartAddress;
  5919. pTask->pProcessThreadInfo[nThreads].ClientId.UniqueProcess =
  5920. pSysThreadInfo->ClientId.UniqueProcess;
  5921. pTask->pProcessThreadInfo[nThreads].ClientId.UniqueThread =
  5922. pSysThreadInfo->ClientId.UniqueThread;
  5923. pTask->pProcessThreadInfo[nThreads].Priority = pSysThreadInfo->Priority;
  5924. pTask->pProcessThreadInfo[nThreads].BasePriority = pSysThreadInfo->BasePriority;
  5925. pTask->pProcessThreadInfo[nThreads].ContextSwitches = pSysThreadInfo->ContextSwitches;
  5926. pTask->pProcessThreadInfo[nThreads].ThreadState = pSysThreadInfo->ThreadState;
  5927. pTask->pProcessThreadInfo[nThreads].WaitReason = pSysThreadInfo->WaitReason;
  5928. pSysThreadInfo++;
  5929. }
  5930. }
  5931. }
  5932. }
  5933. pTask++;
  5934. totalTasks++;
  5935. if ( ProcessInfo->NextEntryOffset == 0 ){
  5936. break;
  5937. }
  5938. TotalOffset += ProcessInfo->NextEntryOffset;
  5939. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&CommonLargeBuffer[TotalOffset];
  5940. }while ( totalTasks < NumTasks );
  5941. if(CommonLargeBuffer != NULL){
  5942. VirtualFree(CommonLargeBuffer, 0, MEM_RELEASE);
  5943. }
  5944. return totalTasks;
  5945. } //getTasklist()
  5946. VOID
  5947. SetProfileSourcesRates(
  5948. PPROC_TO_MONITOR ProcToMonitor
  5949. )
  5950. /*++
  5951. Routine Description:
  5952. Attempts to set the requested (or default) sampling rates for the selected profile sources
  5953. Will try to set the system default rate if it failed to set the requested rate for a particular source
  5954. Arguments:
  5955. ProcToMonitor - Pointer to the structure of the process being monitored
  5956. Return Value:
  5957. None.
  5958. --*/
  5959. {
  5960. KPROFILE_SOURCE ProfileSource;
  5961. NTSTATUS Status;
  5962. ULONG ProfileSourceIndex;
  5963. ULONGLONG Pid;
  5964. CHAR String1[] = "\nKernel Profile (PID = %I64d): Source=";
  5965. CHAR String2[] = "\nPID = %I64d: Source=";
  5966. CHAR OutString[256] = "";
  5967. // Update profiling rate for kernel or user processes traces if necessary
  5968. for (ProfileSourceIndex = 0; ProfileSourceIndex < gSourceMaximum; ProfileSourceIndex++){
  5969. if (gpProcDummy->Source[ProfileSourceIndex].Interval != 0){
  5970. Pid = ProcToMonitor->Pid;
  5971. if( ProcToMonitor->ProcessHandle == SYM_KERNEL_HANDLE ) {
  5972. sprintf( &OutString[0], String1, Pid);
  5973. } else {
  5974. sprintf( &OutString[0], String2, Pid);
  5975. }
  5976. ProcToMonitor->Source[ProfileSourceIndex].Interval = gpProcDummy->Source[ProfileSourceIndex].Interval;
  5977. if ( ProcToMonitor->Source[ProfileSourceIndex].DesiredInterval && ProcToMonitor->Source[ProfileSourceIndex].Interval ) {
  5978. ULONG ThisInterval;
  5979. ProfileSource = ProcToMonitor->Source[ProfileSourceIndex].ProfileSource;
  5980. NtSetIntervalProfile(ProcToMonitor->Source[ProfileSourceIndex].Interval, ProfileSource);
  5981. Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
  5982. if(gVerbose & VERBOSE_PROFILING )
  5983. FPRINTF(stdout, "Requested Rate= %ld Events/Hit, Actual Rate= %ld Events/Hit\n",
  5984. ProcToMonitor->Source[ProfileSourceIndex].Interval,
  5985. ThisInterval
  5986. );
  5987. if ((NT_SUCCESS(Status)) && RATES_MATCH(ThisInterval, gpProcDummy->Source[ProfileSourceIndex].Interval) ) {
  5988. if ( ProfileSourceIndex < gStaticCount ) {
  5989. if ( ProcToMonitor->Source[ProfileSourceIndex].Interval == gStaticSource[ProfileSourceIndex].Interval ){
  5990. if (ThisInterval == ProcToMonitor->Source[ProfileSourceIndex].Interval){
  5991. FPRINTF(stdout,
  5992. "%s %s, \nUsing Kernrate Default Rate of %ld events/hit\n",
  5993. OutString,
  5994. ProcToMonitor->Source[ProfileSourceIndex].Name,
  5995. ThisInterval
  5996. );
  5997. } else {
  5998. FPRINTF(stdout,
  5999. "%s, %s, \nTried Using Kernrate Default Rate of %ld events/hit, Actual Rate= %ld events/hit\n",
  6000. OutString,
  6001. ProcToMonitor->Source[ProfileSourceIndex].Name,
  6002. ProcToMonitor->Source[ProfileSourceIndex].Interval,
  6003. ThisInterval
  6004. );
  6005. }
  6006. } else {
  6007. FPRINTF(stdout,
  6008. "%s %s, \nUser Requested Rate= %ld events/hit, Actual Rate= %ld events/hit\n",
  6009. OutString,
  6010. ProcToMonitor->Source[ProfileSourceIndex].Name,
  6011. ProcToMonitor->Source[ProfileSourceIndex].Interval,
  6012. ThisInterval
  6013. );
  6014. }
  6015. } else {
  6016. FPRINTF(stdout,
  6017. "%s %s, \nUsing Kernrate Default or User Requested Rate of %ld events/hit\n",
  6018. OutString,
  6019. ProcToMonitor->Source[ProfileSourceIndex].Name,
  6020. ThisInterval
  6021. );
  6022. }
  6023. ProcToMonitor->Source[ProfileSourceIndex].Interval = ThisInterval;
  6024. } else {
  6025. NtSetIntervalProfile(ProcToMonitor->Source[ProfileSourceIndex].DesiredInterval, ProfileSource);
  6026. Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
  6027. if ((NT_SUCCESS(Status)) && (ThisInterval > 0)) {
  6028. BOOL bPrint = TRUE;
  6029. //
  6030. // The StaticSources array (may) contain invalid default intervals, let's not bother the user with that
  6031. //
  6032. if ( ProfileSourceIndex < gStaticCount ) {
  6033. if ( ProcToMonitor->Source[ProfileSourceIndex].Interval == gStaticSource[ProfileSourceIndex].Interval ){
  6034. FPRINTF(stdout,
  6035. "%s %s, \nUsing Kernrate Default Rate of %ld events/hit\n",
  6036. OutString,
  6037. ProcToMonitor->Source[ProfileSourceIndex].Name,
  6038. ThisInterval
  6039. );
  6040. bPrint = FALSE;
  6041. }
  6042. }
  6043. if(bPrint == TRUE) {
  6044. FPRINTF(stdout,
  6045. "%s %s, \nCould Not Set User Requested Rate, Using System Default Rate of %ld events/hit\n",
  6046. OutString,
  6047. ProcToMonitor->Source[ProfileSourceIndex].Name,
  6048. ThisInterval
  6049. );
  6050. }
  6051. ProcToMonitor->Source[ProfileSourceIndex].Interval = ThisInterval;
  6052. } else {
  6053. ProcToMonitor->Source[ProfileSourceIndex].Interval = 0;
  6054. FPRINTF(stdout,
  6055. "%s %s, Could not Set Interval Rate, Setting to 0 (disabling this source)\n",
  6056. OutString,
  6057. ProcToMonitor->Source[ProfileSourceIndex].Name
  6058. );
  6059. }
  6060. }
  6061. } else {
  6062. ProcToMonitor->Source[ProfileSourceIndex].Interval = 0;
  6063. }
  6064. } else if( ProfileSourceIndex == SOURCE_TIME ) {
  6065. ULONG ThisInterval;
  6066. ProfileSource = ProcToMonitor->Source[ProfileSourceIndex].ProfileSource;
  6067. NtSetIntervalProfile(ProcToMonitor->Source[ProfileSourceIndex].Interval, ProfileSource);
  6068. Status = NtQueryIntervalProfile(ProfileSource, &ThisInterval);
  6069. if ((NT_SUCCESS(Status)) && (ThisInterval == 0)) {
  6070. FPRINTF(stdout,
  6071. "CPU TIME source disabled per user request\n"
  6072. );
  6073. } else {
  6074. FPRINTF(stderr,
  6075. "KERNRATE: Could not disable CPU TIME source on this platform (Kernrate will just not profile it)\n"
  6076. );
  6077. }
  6078. ProcToMonitor->Source[ProfileSourceIndex].Interval = 0;
  6079. }
  6080. } //for
  6081. } //SetProfileSourcesRates()
  6082. VOID
  6083. GetProcessLocksInformation (
  6084. PPROC_TO_MONITOR ProcToMonitor,
  6085. ULONG Flags,
  6086. ACTION_TYPE Action
  6087. )
  6088. /*++
  6089. Routine Description:
  6090. Gets and outputs the information about process locks that are under contention
  6091. The list will miss short-lived locks that were created after the start count but went away before the end count.
  6092. Arguments:
  6093. ProcToMonitor - Pointer to the structure of the process being monitored
  6094. Flags - RTL_QUERY_PROCESS_LOCKS
  6095. Action - Either START, STOP or OUTPUT
  6096. Return Value:
  6097. None.
  6098. --*/
  6099. {
  6100. NTSTATUS Status;
  6101. ULONG BufferSize = 0;
  6102. if ( !WIN2K_OS )
  6103. Flags |= RTL_QUERY_PROCESS_NONINVASIVE; //Not defined on Win2K
  6104. switch (Action) {
  6105. case START:
  6106. ProcToMonitor->pProcDebugInfoStart = RtlCreateQueryDebugBuffer( BufferSize, FALSE );
  6107. if(ProcToMonitor->pProcDebugInfoStart == NULL) {
  6108. FPRINTF(stderr, "KERNRATE: Failed to create buffer (START) in GetProcessLocksInformation\n");
  6109. FPRINTF(stderr, "KERNRATE: Process %s may have insufficient virtual memory left for collecting locks information\n",
  6110. ProcToMonitor->ProcessName
  6111. );
  6112. return;
  6113. }
  6114. Status = RtlQueryProcessDebugInformation( (HANDLE)ProcToMonitor->Pid,
  6115. Flags,
  6116. ProcToMonitor->pProcDebugInfoStart
  6117. );
  6118. if(!NT_SUCCESS(Status)) {
  6119. FPRINTF(stderr, "KERNRATE: Failed call to RtlQueryProcessDebugInformation (START) for Locks Information\n");
  6120. FPRINTF(stderr, "Process: %s, Status = %x\n", ProcToMonitor->ProcessName, Status);
  6121. if(Status == STATUS_INFO_LENGTH_MISMATCH)FPRINTF(stderr, "Status = INFO_LENGTH_MISMATCH\n");
  6122. if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, "Status = NO_MEMORY\n");
  6123. return;
  6124. }
  6125. break;
  6126. case STOP:
  6127. ProcToMonitor->pProcDebugInfoStop = RtlCreateQueryDebugBuffer( BufferSize, FALSE );
  6128. if(ProcToMonitor->pProcDebugInfoStop == NULL) {
  6129. FPRINTF(stderr, "KERNRATE: Failed to create buffer (STOP) in GetProcessLocksInformation\n");
  6130. FPRINTF(stderr, "KERNRATE: Process %s may have insufficient virtual memory left for collecting locks information\n",
  6131. ProcToMonitor->ProcessName
  6132. );
  6133. return;
  6134. }
  6135. Status = RtlQueryProcessDebugInformation( (HANDLE)ProcToMonitor->Pid,
  6136. Flags,
  6137. ProcToMonitor->pProcDebugInfoStop
  6138. );
  6139. if(!NT_SUCCESS(Status)){
  6140. FPRINTF(stderr, "KERNRATE: Failed call to RtlQueryProcessDebugInformation (STOP) for Locks Information\n");
  6141. FPRINTF(stderr, "Process: %s, Status = %x\n", ProcToMonitor->ProcessName, Status);
  6142. if(Status == STATUS_INFO_LENGTH_MISMATCH)FPRINTF(stderr, "Status = INFO_LENGTH_MISMATCH\n");
  6143. if(Status == STATUS_NO_MEMORY)FPRINTF(stderr, "Status = NO_MEMORY\n");
  6144. return;
  6145. }
  6146. break;
  6147. case OUTPUT:
  6148. {
  6149. if( ProcToMonitor->pProcDebugInfoStart == NULL || ProcToMonitor->pProcDebugInfoStop == NULL) return;
  6150. OutputLocksInformation( ProcToMonitor->pProcDebugInfoStart->Locks,
  6151. ProcToMonitor->pProcDebugInfoStop->Locks,
  6152. ProcToMonitor
  6153. );
  6154. //
  6155. // Cleanup for this process - note that the target process may be gone so we need to be careful
  6156. //
  6157. try {
  6158. if ( ProcToMonitor->pProcDebugInfoStart != NULL)
  6159. RtlDestroyQueryDebugBuffer( ProcToMonitor->pProcDebugInfoStart );
  6160. if ( ProcToMonitor->pProcDebugInfoStop != NULL)
  6161. RtlDestroyQueryDebugBuffer( ProcToMonitor->pProcDebugInfoStop );
  6162. } _except ( EXCEPTION_EXECUTE_HANDLER ) {
  6163. FPRINTF(stderr, "Exception %X raised while trying to call RtlDestroyQueryDebugBuffer\n",
  6164. _exception_code()
  6165. );
  6166. FPRINTF(stderr, "This could happen if the monitored process is gone\n");
  6167. return;
  6168. }
  6169. }
  6170. break;
  6171. default:
  6172. FPRINTF(stderr, "GetProcessLocksInformation was called with an invalid Action parameter - %d\n", Action);
  6173. }
  6174. } //GetProcessLocksInformation ()
  6175. VOID
  6176. GetSystemLocksInformation (
  6177. ACTION_TYPE Action
  6178. )
  6179. /*++
  6180. Routine Description:
  6181. Gets and outputs the information about System (Kernel) locks that are under contention
  6182. The list will miss short-lived locks that were created after the start count but went away before the end count.
  6183. Arguments:
  6184. Action - Either START, STOP or OUTPUT
  6185. Return Value:
  6186. None.
  6187. --*/
  6188. {
  6189. NTSTATUS Status;
  6190. static BOOL bDisplayLockInfo;
  6191. static PRTL_PROCESS_LOCKS ProcessLockInfoStart;
  6192. static PRTL_PROCESS_LOCKS ProcessLockInfoStop;
  6193. ULONG BufferSize = sizeof(RTL_PROCESS_LOCKS);
  6194. switch (Action) {
  6195. case START:
  6196. bDisplayLockInfo = TRUE;
  6197. do {
  6198. ProcessLockInfoStart = malloc(BufferSize);
  6199. if(ProcessLockInfoStart == NULL)
  6200. {
  6201. FPRINTF(stderr, "KERNRATE: Failed to allocate Buffer for Lock Info (START) \n");
  6202. bDisplayLockInfo = FALSE;
  6203. return;
  6204. }
  6205. Status = NtQuerySystemInformation(SystemLocksInformation,
  6206. ProcessLockInfoStart,
  6207. BufferSize,
  6208. &BufferSize
  6209. );
  6210. if(Status == STATUS_SUCCESS){
  6211. break;
  6212. }
  6213. if(Status != STATUS_INFO_LENGTH_MISMATCH){
  6214. FPRINTF(stderr, "KERNRATE: Failed call to NTQuerySystemInformation for Lock Info (START) \n");
  6215. bDisplayLockInfo = FALSE;
  6216. return;
  6217. }
  6218. free( ProcessLockInfoStart );
  6219. ProcessLockInfoStart = NULL;
  6220. }while (Status == STATUS_INFO_LENGTH_MISMATCH);
  6221. break;
  6222. case STOP:
  6223. do {
  6224. ProcessLockInfoStop = malloc(BufferSize);
  6225. if(ProcessLockInfoStop == NULL)
  6226. {
  6227. FPRINTF(stderr, "KERNRATE: Failed to allocate Buffer for Lock Info (STOP) \n");
  6228. bDisplayLockInfo = FALSE;
  6229. return;
  6230. }
  6231. Status = NtQuerySystemInformation( SystemLocksInformation,
  6232. ProcessLockInfoStop,
  6233. BufferSize,
  6234. &BufferSize
  6235. );
  6236. if(Status == STATUS_SUCCESS){
  6237. break;
  6238. }
  6239. if(Status != STATUS_INFO_LENGTH_MISMATCH){
  6240. FPRINTF(stderr, "KERNRATE: Failed call to NTQuerySystemInformation for Lock Info (STOP) \n");
  6241. bDisplayLockInfo = FALSE;
  6242. return;
  6243. }
  6244. free( ProcessLockInfoStop );
  6245. ProcessLockInfoStop = NULL;
  6246. }while (Status == STATUS_INFO_LENGTH_MISMATCH);
  6247. break;
  6248. case OUTPUT:
  6249. if( bDisplayLockInfo == TRUE ){
  6250. OutputLocksInformation( ProcessLockInfoStart,
  6251. ProcessLockInfoStop,
  6252. gpSysProc
  6253. );
  6254. //
  6255. // Cleanup
  6256. //
  6257. if(ProcessLockInfoStart != NULL){
  6258. free( ProcessLockInfoStart );
  6259. ProcessLockInfoStart = NULL;
  6260. }
  6261. if(ProcessLockInfoStop != NULL){
  6262. free( ProcessLockInfoStop );
  6263. ProcessLockInfoStop = NULL;
  6264. }
  6265. }
  6266. break;
  6267. default:
  6268. FPRINTF(stderr, "KERNRATE INTERNAL ERROR: GetSystemLocksInformation was called with an invalid Action parameter - %d\n",
  6269. Action
  6270. );
  6271. }
  6272. } //GetSystemLocksInformation ()
  6273. VOID
  6274. OutputLocksInformation(
  6275. PRTL_PROCESS_LOCKS pLockInfoStart,
  6276. PRTL_PROCESS_LOCKS pLockInfoStop,
  6277. PPROC_TO_MONITOR Proc
  6278. )
  6279. /*++
  6280. Routine Description:
  6281. Outputs Lock Contention information for either System (Kernel) or User Process Locks
  6282. If a lock is new (has only final counts) or gone (has only initial counts, it will be marked accordingly
  6283. The routine will try to get the symbol name associated with the lock if one exists.
  6284. The user can control (filter) the output by changing gLockContentionMinCount
  6285. The list will miss short-lived Locks that were created after the start count but went away before the end count.
  6286. Arguments:
  6287. pLockInfoStart - Pointer to lock info struct (Initial count)
  6288. pLockInfoStop - Pointer to lock info struct (Final count)
  6289. SymHandle - Symbol Handle to the process
  6290. Return Value:
  6291. None.
  6292. --*/
  6293. {
  6294. ULONG i,j,k;
  6295. DWORD64 disp;
  6296. CHAR TypeString[32] = "UNKNOWN";
  6297. PMODULE Module;
  6298. BOOL bAnyLocksFound = FALSE;
  6299. BOOL *Index = NULL;
  6300. ULONG64 *LoadedBases = NULL;
  6301. HANDLE SymHandle;
  6302. if(Proc == NULL){
  6303. FPRINTF(stderr, "KERNRATE: NULL Process pointer passed to OutputLocksInformation\n");
  6304. FPRINTF(stderr, "Possible cause: Kernel Resource info was required with the -x or -xk command line options,\n");
  6305. FPRINTF(stderr, "but was the -a option specified on the command line as well?\n");
  6306. exit(1);
  6307. }
  6308. SymHandle = Proc->ProcessHandle;
  6309. if( pLockInfoStart != NULL ) {
  6310. if( pLockInfoStart->NumberOfLocks > 0 ) {
  6311. Index = (BOOL*)calloc(pLockInfoStart->NumberOfLocks, sizeof(BOOL));
  6312. if( Index == NULL ){
  6313. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for Index data in OutputLocksInformation\n");
  6314. exit(1);
  6315. }
  6316. } else {
  6317. FPRINTF(stdout, "\nNo locks found or process %s has insufficient virtual memory for collecting lock information\n",
  6318. Proc->ProcessName
  6319. );
  6320. return;
  6321. }
  6322. }
  6323. FPRINTF(stdout,
  6324. "\nLocks Contention Info:\n\nAddress, Contention-Diff., Rate(per sec.), Thread, Type, Recursion, Waiting-Shared, Waiting-Exclusive, Symbol-Information\n"
  6325. );
  6326. // We could sort the data first for a faster search, but sorting the two arrays costs too...
  6327. if( pLockInfoStop != NULL ) {
  6328. LoadedBases = (ULONG64 *)calloc( Proc->ModuleCount, sizeof(ULONG64) );
  6329. if( LoadedBases == NULL ){
  6330. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for module base data in OutputLocksInformation\n");
  6331. exit(1);
  6332. }
  6333. for (i=0; i < pLockInfoStop->NumberOfLocks; i++){
  6334. BOOL bFound = FALSE;
  6335. if( pLockInfoStop->Locks[i].ContentionCount >= gLockContentionMinCount) //Additional preliminary filter
  6336. //(if not true then contention difference not true)
  6337. if( pLockInfoStart != NULL ) {
  6338. for (j=0; j < pLockInfoStart->NumberOfLocks; j++){
  6339. if( (DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address == (DWORD64)(DWORD64 *)pLockInfoStart->Locks[j].Address ){
  6340. LONG ContentionDiff = pLockInfoStop->Locks[i].ContentionCount -
  6341. pLockInfoStart->Locks[j].ContentionCount;
  6342. long double Rate = (long double)ContentionDiff / gldElapsedSeconds;
  6343. LONG RecursionDiff = pLockInfoStop->Locks[i].RecursionCount -
  6344. pLockInfoStart->Locks[j].RecursionCount;
  6345. LONG WaitShrdDiff = pLockInfoStop->Locks[i].NumberOfWaitingShared -
  6346. pLockInfoStart->Locks[j].NumberOfWaitingShared;
  6347. LONG WaitExclDiff = pLockInfoStop->Locks[i].NumberOfWaitingExclusive -
  6348. pLockInfoStart->Locks[j].NumberOfWaitingExclusive;
  6349. if(ContentionDiff >= (LONG)gLockContentionMinCount ){
  6350. if(pLockInfoStop->Locks[i].Type == RTL_CRITSECT_TYPE)
  6351. strncpy(TypeString, "CRITICAL_SECTION", sizeof(TypeString)-1);
  6352. if(pLockInfoStop->Locks[i].Type == RTL_RESOURCE_TYPE)
  6353. strncpy(TypeString, "RESOURCE", sizeof(TypeString)-1);
  6354. FPRINTF(stdout, "%p, %10ld, %10.0f, 0x%I64x, %s, ",
  6355. (PVOID64)pLockInfoStop->Locks[i].Address,
  6356. ContentionDiff,
  6357. Rate,
  6358. (LONGLONG)pLockInfoStop->Locks[i].OwningThread,
  6359. TypeString
  6360. );
  6361. if(pLockInfoStop->Locks[i].Type == RTL_CRITSECT_TYPE)
  6362. FPRINTF(stdout, " %10ld, N/A, N/A", RecursionDiff);
  6363. if(pLockInfoStop->Locks[i].Type == RTL_RESOURCE_TYPE)
  6364. FPRINTF(stdout, " N/A, %10ld, %10ld ", WaitShrdDiff, WaitExclDiff);
  6365. Module = FindModuleForAddress64( Proc,
  6366. (DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address);
  6367. //
  6368. //In the folllowing it may happen that we try to load a module more than once
  6369. //we don't really care about the return status
  6370. //
  6371. if( Module != NULL ) {
  6372. if( Module->bZoom != TRUE ){
  6373. //
  6374. //Avoid trying to load what's already loaded and for later cleanup purposes
  6375. //
  6376. for( k=0; k<Proc->ModuleCount; k++ ) {
  6377. if(LoadedBases[k] == Module->Base) { //Already been loaded
  6378. break;
  6379. } else if( LoadedBases[k] == 0 ) { //End of populated list, load a new module
  6380. (void)SymLoadModule64( SymHandle, // hProcess
  6381. NULL, // hFile [for Debugger]
  6382. Module->module_Name, // ImageName
  6383. NULL, // ModuleName
  6384. Module->Base, // BaseOfDll
  6385. Module->Length // SizeOfDll
  6386. );
  6387. *LoadedBases = Module->Base;
  6388. ++LoadedBases;
  6389. break;
  6390. }
  6391. }
  6392. }
  6393. if ( Module->Base )
  6394. FPRINTF(stdout, " ,base= %p", (PVOID64)Module->Base);
  6395. if ( Module->module_Name )
  6396. FPRINTF(stdout, " - %s", Module->module_Name);
  6397. }
  6398. if ( SymGetSymFromAddr64((HANDLE)SymHandle, (DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address, &disp, gSymbol )){
  6399. FPRINTF(stdout, "!%s\n", gSymbol->Name);
  6400. } else {
  6401. FPRINTF(stdout, "\n");
  6402. }
  6403. bAnyLocksFound = TRUE;
  6404. }
  6405. Index[j] = TRUE;
  6406. bFound = TRUE;
  6407. break;
  6408. }
  6409. }//for(j)
  6410. }//if(pLockInfoStart)
  6411. //
  6412. // No matching address found for START, therefore this is a NEW lock
  6413. // Note that here the additional filter is checked anyway at the beginning
  6414. //
  6415. if( !bFound && pLockInfoStop->Locks[i].ContentionCount >= gLockContentionMinCount ) {
  6416. long double Rate = (long double)pLockInfoStop->Locks[i].ContentionCount / gldElapsedSeconds;
  6417. FPRINTF(stdout, "%p, %10Ld, %10.0f, 0x%I64x, %s, ",
  6418. (PVOID64)pLockInfoStop->Locks[i].Address,
  6419. pLockInfoStop->Locks[i].ContentionCount,
  6420. Rate,
  6421. (LONGLONG)pLockInfoStop->Locks[i].OwningThread,
  6422. TypeString
  6423. );
  6424. if( pLockInfoStop->Locks[i].Type == RTL_CRITSECT_TYPE )
  6425. FPRINTF(stdout, " %10Ld, N/A, N/A",
  6426. pLockInfoStop->Locks[i].RecursionCount);
  6427. if( pLockInfoStop->Locks[i].Type == RTL_RESOURCE_TYPE )
  6428. FPRINTF(stdout, " N/A, %10Ld, %10Ld ",
  6429. pLockInfoStop->Locks[i].NumberOfWaitingShared,
  6430. pLockInfoStop->Locks[i].NumberOfWaitingExclusive
  6431. );
  6432. Module = FindModuleForAddress64( Proc,
  6433. (DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address);
  6434. if( Module != NULL ) {
  6435. if( Module->bZoom != TRUE ){
  6436. //
  6437. //Avoid trying to load what's already loaded and for later cleanup purposes
  6438. //
  6439. for( k=0; k<Proc->ModuleCount; k++ ) {
  6440. if(LoadedBases[k] == Module->Base) { //Already been loaded
  6441. break;
  6442. } else {
  6443. if( LoadedBases[k] == 0 ) { //End of populated list, load a new module
  6444. (void)SymLoadModule64( SymHandle, // hProcess
  6445. NULL, // hFile [for Debugger]
  6446. Module->module_Name, // ImageName
  6447. NULL, // ModuleName
  6448. Module->Base, // BaseOfDll
  6449. Module->Length // SizeOfDll
  6450. );
  6451. *LoadedBases = Module->Base;
  6452. ++LoadedBases;
  6453. break;
  6454. }
  6455. }
  6456. }
  6457. }
  6458. if ( Module->Base )
  6459. FPRINTF(stdout, ", base= %p", (PVOID64)Module->Base);
  6460. if ( Module->module_Name )
  6461. FPRINTF(stdout, " - %s", Module->module_Name);
  6462. }
  6463. if ( SymGetSymFromAddr64((HANDLE)SymHandle,(DWORD64)(DWORD64 *)pLockInfoStop->Locks[i].Address, &disp, gSymbol )){
  6464. FPRINTF(stdout, "!%s, NEW - (actual rate could be higher)\n", gSymbol->Name);
  6465. } else {
  6466. FPRINTF(stdout, ", NEW - (actual rate could be higher)\n");
  6467. }
  6468. bAnyLocksFound = TRUE;
  6469. }
  6470. }// for(i)
  6471. // Cleanup
  6472. for( i=0; i<Proc->ModuleCount; i++ )
  6473. {
  6474. if(LoadedBases[i] != 0) {
  6475. if(!SymUnloadModule64( SymHandle, LoadedBases[i])) {
  6476. VerbosePrint(( VERBOSE_IMAGEHLP, "Kernrate: Could Not Unload Module, Base= %p for Process %s\n",
  6477. (PVOID64)LoadedBases[i],
  6478. Proc->ProcessName
  6479. ));
  6480. continue;
  6481. }
  6482. VerbosePrint(( VERBOSE_IMAGEHLP, "Kernrate: Unloaded Module, Base= %p for Process %s\n",
  6483. (PVOID64)LoadedBases[i],
  6484. Proc->ProcessName
  6485. ));
  6486. }
  6487. }
  6488. }//if(pLockInfoStop)
  6489. //
  6490. // No matching address found for STOP, therefore this lock is GONE
  6491. //
  6492. if( pLockInfoStart != NULL) {
  6493. for (j=0; j < pLockInfoStart->NumberOfLocks; j++){
  6494. if( (Index[j] == FALSE) && (pLockInfoStart->Locks[j].ContentionCount >= gLockContentionMinCount) ){
  6495. if(pLockInfoStart->Locks[j].Type == RTL_CRITSECT_TYPE)
  6496. strncpy(TypeString, "CRITICAL_SECTION", sizeof(TypeString)-1);
  6497. if(pLockInfoStart->Locks[j].Type == RTL_RESOURCE_TYPE)
  6498. strncpy(TypeString, "RESOURCE", sizeof(TypeString)-1);
  6499. FPRINTF(stdout, "%p, %10Ld, N/A, 0x%I64x, %s, ",
  6500. (PVOID64)pLockInfoStart->Locks[j].Address,
  6501. pLockInfoStart->Locks[j].ContentionCount,
  6502. (LONGLONG)pLockInfoStart->Locks[j].OwningThread,
  6503. TypeString
  6504. );
  6505. FPRINTF(stdout, " GONE\n");
  6506. bAnyLocksFound = TRUE;
  6507. }
  6508. }//for(j)
  6509. }//if(pLockInfoStart)
  6510. if ( bAnyLocksFound == FALSE)
  6511. FPRINTF(stdout, "\nNo Locks with a contention-count difference of at least %d were found\n", gLockContentionMinCount);
  6512. //
  6513. //Cleanup
  6514. //
  6515. if(Index != NULL){
  6516. free(Index);
  6517. Index = NULL;
  6518. }
  6519. if(LoadedBases != NULL){
  6520. free(LoadedBases);
  6521. LoadedBases = NULL;
  6522. }
  6523. } //OutputLocksInformation()
  6524. VOID
  6525. GetProfileSystemInfo(
  6526. ACTION_TYPE Action
  6527. )
  6528. /*++
  6529. Routine Description:
  6530. Gets and outputs System-Wide performance counts (context-switches, I/O, etc.)
  6531. for the duration of the run
  6532. Arguments:
  6533. Action - Either START, STOP or OUTPUT
  6534. Return Value:
  6535. None.
  6536. --*/
  6537. {
  6538. NTSTATUS Status;
  6539. static BOOL bDisplayPerfInfo;
  6540. static PSYSTEM_PERFORMANCE_INFORMATION SysPerfInfoStart;
  6541. static PSYSTEM_PERFORMANCE_INFORMATION SysPerfInfoStop;
  6542. switch (Action) {
  6543. case START:
  6544. bDisplayPerfInfo = TRUE;
  6545. SysPerfInfoStart = malloc(sizeof(SYSTEM_PERFORMANCE_INFORMATION));
  6546. if(SysPerfInfoStart == NULL){
  6547. FPRINTF(stderr, "Memory allocation failed for SystemPerformanceInformation in GetProfileSystemInfo\n");
  6548. exit(1);
  6549. }
  6550. Status = NtQuerySystemInformation(SystemPerformanceInformation,
  6551. SysPerfInfoStart,
  6552. sizeof(SYSTEM_PERFORMANCE_INFORMATION),
  6553. NULL);
  6554. if (!NT_SUCCESS(Status)) {
  6555. FPRINTF(stderr, "QuerySystemInformation for performance information(1) failed %08lx\n", Status);
  6556. bDisplayPerfInfo = FALSE;
  6557. }
  6558. break;
  6559. case STOP:
  6560. SysPerfInfoStop = malloc(sizeof(SYSTEM_PERFORMANCE_INFORMATION));
  6561. if(SysPerfInfoStop == NULL){
  6562. FPRINTF(stderr, "Memory allocation failed for SystemPerformanceInformation in GetProfileSystemInfo\n");
  6563. exit(1);
  6564. }
  6565. Status = NtQuerySystemInformation(SystemPerformanceInformation,
  6566. SysPerfInfoStop,
  6567. sizeof(SYSTEM_PERFORMANCE_INFORMATION),
  6568. NULL);
  6569. if (!NT_SUCCESS(Status)) {
  6570. FPRINTF(stderr, "QuerySystemInformation for performance information(2) failed %08lx\n", Status);
  6571. bDisplayPerfInfo = FALSE;
  6572. }
  6573. break;
  6574. case OUTPUT:
  6575. if( bDisplayPerfInfo == TRUE ){
  6576. FPRINTF (stdout, "\n Total Avg. Rate\n");
  6577. DisplayTotalAndRate( SysPerfInfoStart->ContextSwitches,
  6578. SysPerfInfoStop->ContextSwitches,
  6579. gldElapsedSeconds,
  6580. "Context Switches",
  6581. "sec."
  6582. );
  6583. DisplayTotalAndRate( SysPerfInfoStart->SystemCalls,
  6584. SysPerfInfoStop->SystemCalls,
  6585. gldElapsedSeconds,
  6586. "System Calls",
  6587. "sec."
  6588. );
  6589. DisplayTotalAndRate( SysPerfInfoStart->PageFaultCount,
  6590. SysPerfInfoStop->PageFaultCount,
  6591. gldElapsedSeconds,
  6592. "Page Faults",
  6593. "sec."
  6594. );
  6595. DisplayTotalAndRate( SysPerfInfoStart->IoReadOperationCount,
  6596. SysPerfInfoStop->IoReadOperationCount,
  6597. gldElapsedSeconds,
  6598. "I/O Read Operations",
  6599. "sec."
  6600. );
  6601. DisplayTotalAndRate( SysPerfInfoStart->IoWriteOperationCount,
  6602. SysPerfInfoStop->IoWriteOperationCount,
  6603. gldElapsedSeconds,
  6604. "I/O Write Operations",
  6605. "sec."
  6606. );
  6607. DisplayTotalAndRate( SysPerfInfoStart->IoOtherOperationCount,
  6608. SysPerfInfoStop->IoOtherOperationCount,
  6609. gldElapsedSeconds,
  6610. "I/O Other Operations",
  6611. "sec."
  6612. );
  6613. DisplayTotalAndRate( SysPerfInfoStart->IoReadTransferCount.QuadPart,
  6614. SysPerfInfoStop->IoReadTransferCount.QuadPart,
  6615. (long double)(SysPerfInfoStop->IoReadOperationCount - SysPerfInfoStart->IoReadOperationCount),
  6616. "I/O Read Bytes",
  6617. " I/O"
  6618. );
  6619. DisplayTotalAndRate( SysPerfInfoStart->IoWriteTransferCount.QuadPart,
  6620. SysPerfInfoStop->IoWriteTransferCount.QuadPart,
  6621. (long double)(SysPerfInfoStop->IoWriteOperationCount - SysPerfInfoStart->IoWriteOperationCount),
  6622. "I/O Write Bytes",
  6623. " I/O"
  6624. );
  6625. DisplayTotalAndRate( SysPerfInfoStart->IoOtherTransferCount.QuadPart,
  6626. SysPerfInfoStop->IoOtherTransferCount.QuadPart,
  6627. (long double)(SysPerfInfoStop->IoOtherOperationCount - SysPerfInfoStart->IoOtherOperationCount),
  6628. "I/O Other Bytes",
  6629. " I/O"
  6630. );
  6631. }
  6632. //
  6633. // Cleanup
  6634. //
  6635. if(SysPerfInfoStart != NULL){
  6636. free(SysPerfInfoStart);
  6637. SysPerfInfoStart = NULL;
  6638. }
  6639. if(SysPerfInfoStop != NULL){
  6640. free(SysPerfInfoStop);
  6641. SysPerfInfoStop = NULL;
  6642. }
  6643. break;
  6644. default:
  6645. FPRINTF(stderr, "GetProfileSystemInfo was called with an invalid Action parameter - %d\n", Action);
  6646. }
  6647. } //GetProfileSystemInfo()
  6648. VOID
  6649. DisplayTotalAndRate (
  6650. LONGLONG StartCount,
  6651. LONGLONG StopCount,
  6652. long double RateAgainst,
  6653. PCHAR CounterName,
  6654. PCHAR RateAgainstUnits
  6655. )
  6656. {
  6657. long double Rate;
  6658. LARGE_INTEGER Total;
  6659. Total.QuadPart = StopCount - StartCount;
  6660. Rate = RateAgainst > 0? (long double)Total.QuadPart/RateAgainst : 0;
  6661. FPRINTF(stdout, " %-21s, %12I64d, %.0f/%s\n",
  6662. CounterName,
  6663. Total.QuadPart,
  6664. Rate,
  6665. RateAgainstUnits
  6666. );
  6667. } //DisplayTotalAndRate()
  6668. VOID
  6669. OutputStartStopValues (
  6670. SIZE_T StartCount,
  6671. SIZE_T StopCount,
  6672. PCHAR CounterName
  6673. )
  6674. {
  6675. LARGE_INTEGER StartValue;
  6676. LARGE_INTEGER StopValue;
  6677. LARGE_INTEGER Diff;
  6678. StartValue.QuadPart = StartCount;
  6679. StopValue.QuadPart = StopCount;
  6680. Diff.QuadPart = StopValue.QuadPart - StartValue.QuadPart;
  6681. FPRINTF(stdout, " %-21s, %15I64d, %15I64d, %15I64d\n",
  6682. CounterName,
  6683. StartValue.QuadPart,
  6684. StopValue.QuadPart,
  6685. Diff.QuadPart
  6686. );
  6687. } //OutputStartStopValues()
  6688. VOID
  6689. OutputPercentValue (
  6690. LONGLONG StartCount,
  6691. LONGLONG StopCount,
  6692. LARGE_INTEGER Base,
  6693. PCHAR CounterName
  6694. )
  6695. {
  6696. long double PercentValue;
  6697. LARGE_INTEGER Diff;
  6698. Diff.QuadPart = StopCount - StartCount;
  6699. PercentValue = Base.QuadPart > 0? 100*(long double)Diff.QuadPart/(long double)Base.QuadPart : 0;
  6700. FPRINTF(stdout, " %-28s= %.2f%% of the Elapsed Time\n",
  6701. CounterName,
  6702. PercentValue
  6703. );
  6704. } //OutputPercentValue()
  6705. VOID
  6706. OutputProcessPerfInfo (
  6707. PTASK_LIST pTask,
  6708. ULONG NumTasks,
  6709. PPROC_TO_MONITOR ProcToMonitor
  6710. )
  6711. /*++
  6712. Routine Description:
  6713. Gets and outputs Process-Specific performance counts (context-switches, I/O, etc.)
  6714. for the duration of the run
  6715. Arguments:
  6716. pTask - A pointer to Kernrate's structure of running tasks
  6717. NumTasks - Number of tasks in kernrate's task list
  6718. ProcToMonitor - Pointer to the process structure
  6719. Return Value:
  6720. None.
  6721. --*/
  6722. {
  6723. DWORD m, k, nThreads;
  6724. LONGLONG Diff;
  6725. if (pTask != NULL) {
  6726. if (ProcToMonitor != NULL){
  6727. for (m=0; m < NumTasks; m++) {
  6728. if (pTask[m].ProcessId == ProcToMonitor->Pid) {
  6729. FPRINTF (stdout, "\n");
  6730. OutputPercentValue( ProcToMonitor->ProcPerfInfoStart.UserTime.QuadPart,
  6731. pTask[m].ProcessPerfInfo.UserTime.QuadPart,
  6732. gTotal2ElapsedTime64,
  6733. "User Time"
  6734. );
  6735. OutputPercentValue( ProcToMonitor->ProcPerfInfoStart.KernelTime.QuadPart,
  6736. pTask[m].ProcessPerfInfo.KernelTime.QuadPart,
  6737. gTotal2ElapsedTime64,
  6738. "Kernel Time"
  6739. );
  6740. FPRINTF (stdout, "\n Total Avg. Rate\n");
  6741. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.PageFaultCount,
  6742. pTask[m].ProcessPerfInfo.PageFaultCount,
  6743. gldElapsedSeconds,
  6744. "Page Faults",
  6745. "sec."
  6746. );
  6747. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.ReadOperationCount.QuadPart,
  6748. pTask[m].ProcessPerfInfo.ReadOperationCount.QuadPart,
  6749. gldElapsedSeconds,
  6750. "I/O Read Operations",
  6751. "sec."
  6752. );
  6753. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.WriteOperationCount.QuadPart,
  6754. pTask[m].ProcessPerfInfo.WriteOperationCount.QuadPart,
  6755. gldElapsedSeconds,
  6756. "I/O Write Operations",
  6757. "sec."
  6758. );
  6759. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.OtherOperationCount.QuadPart,
  6760. pTask[m].ProcessPerfInfo.OtherOperationCount.QuadPart,
  6761. gldElapsedSeconds,
  6762. "I/O Other Operations",
  6763. "sec."
  6764. );
  6765. Diff = pTask[m].ProcessPerfInfo.ReadOperationCount.QuadPart
  6766. - ProcToMonitor->ProcPerfInfoStart.ReadOperationCount.QuadPart;
  6767. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.ReadTransferCount.QuadPart,
  6768. pTask[m].ProcessPerfInfo.ReadTransferCount.QuadPart,
  6769. (long double)Diff,
  6770. "I/O Read Bytes",
  6771. " I/O"
  6772. );
  6773. Diff = pTask[m].ProcessPerfInfo.WriteOperationCount.QuadPart
  6774. - ProcToMonitor->ProcPerfInfoStart.WriteOperationCount.QuadPart;
  6775. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.WriteTransferCount.QuadPart,
  6776. pTask[m].ProcessPerfInfo.WriteTransferCount.QuadPart,
  6777. (long double)Diff,
  6778. "I/O Write Bytes",
  6779. " I/O"
  6780. );
  6781. Diff = pTask[m].ProcessPerfInfo.OtherOperationCount.QuadPart
  6782. - ProcToMonitor->ProcPerfInfoStart.OtherOperationCount.QuadPart;
  6783. DisplayTotalAndRate( ProcToMonitor->ProcPerfInfoStart.OtherTransferCount.QuadPart,
  6784. pTask[m].ProcessPerfInfo.OtherTransferCount.QuadPart,
  6785. (long double)Diff,
  6786. "I/O Other Bytes",
  6787. " I/O"
  6788. );
  6789. FPRINTF (stdout, "\n Start-Count Stop-Count Diff.\n");
  6790. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.NumberOfThreads,
  6791. pTask[m].ProcessPerfInfo.NumberOfThreads,
  6792. "Threads"
  6793. );
  6794. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.HandleCount,
  6795. pTask[m].ProcessPerfInfo.HandleCount,
  6796. "Handles"
  6797. );
  6798. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.WorkingSetSize,
  6799. pTask[m].ProcessPerfInfo.WorkingSetSize,
  6800. "Working Set Bytes"
  6801. );
  6802. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.VirtualSize,
  6803. pTask[m].ProcessPerfInfo.VirtualSize,
  6804. "Virtual Size Bytes"
  6805. );
  6806. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.QuotaPagedPoolUsage,
  6807. pTask[m].ProcessPerfInfo.QuotaPagedPoolUsage,
  6808. "Paged Pool Bytes"
  6809. );
  6810. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.QuotaNonPagedPoolUsage,
  6811. pTask[m].ProcessPerfInfo.QuotaNonPagedPoolUsage,
  6812. "Non Paged Pool Bytes"
  6813. );
  6814. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.PagefileUsage,
  6815. pTask[m].ProcessPerfInfo.PagefileUsage,
  6816. "Pagefile Bytes"
  6817. );
  6818. OutputStartStopValues( ProcToMonitor->ProcPerfInfoStart.PrivatePageCount,
  6819. pTask[m].ProcessPerfInfo.PrivatePageCount,
  6820. "Private Pages Bytes"
  6821. );
  6822. if ( ProcToMonitor->pProcThreadInfoStart != NULL ){
  6823. OutputThreadInfo (pTask,
  6824. m,
  6825. ProcToMonitor
  6826. );
  6827. }
  6828. return;
  6829. }
  6830. }
  6831. FPRINTF(stdout, "\nGeneral Info Unavailable for Process %s (PID= %I64d), because the process is GONE\n",
  6832. ProcToMonitor->ProcessName,
  6833. ProcToMonitor->Pid
  6834. );
  6835. } else {
  6836. FPRINTF(stderr, "Kernrate: OuputProcessPerfInfo - ProcToMonitor is NULL\n");
  6837. }
  6838. } else {
  6839. FPRINTF(stderr, "Kernrate: OuputProcessPerfInfo - pTask is NULL\n");
  6840. }
  6841. } //OutputProcessPerfInfo()
  6842. VOID
  6843. OutputThreadInfo (
  6844. PTASK_LIST pTask,
  6845. DWORD TaskNumber,
  6846. PPROC_TO_MONITOR ProcToMonitor
  6847. )
  6848. /*++
  6849. Routine Description:
  6850. Outputs Thread-Specific counts for a given process
  6851. Arguments:
  6852. pTask - A pointer to Kernrate's structure of running tasks
  6853. TaskNumber - The task index in kernrate's task list
  6854. ProcToMonitor - Pointer to the process structure
  6855. Return Value:
  6856. None.
  6857. --*/
  6858. {
  6859. DWORD m, k, nThreads;
  6860. LONGLONG Diff;
  6861. BOOL bFound;
  6862. BOOL *Index = NULL;
  6863. m = TaskNumber;
  6864. if( ProcToMonitor->ProcPerfInfoStart.NumberOfThreads > 0 ){
  6865. Index = (BOOL*)calloc(ProcToMonitor->ProcPerfInfoStart.NumberOfThreads, sizeof(BOOL));
  6866. if( Index == NULL ){
  6867. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for Index data in OutputThreadInfo\n");
  6868. exit(1);
  6869. }
  6870. }else{
  6871. FPRINTF(stderr, "KERNRATE: No Threads for process %I64d at START???\n", ProcToMonitor->Pid);
  6872. return;
  6873. }
  6874. FPRINTF(stdout, "\n------------- Thread Information ---------------\n");
  6875. FPRINTF (stdout, "\n Start-Count Stop-Count Diff.\n");
  6876. nThreads = pTask[m].ProcessPerfInfo.NumberOfThreads;
  6877. while(nThreads--){
  6878. k = ProcToMonitor->ProcPerfInfoStart.NumberOfThreads;
  6879. bFound = FALSE;
  6880. while( k-- ){
  6881. if( pTask[m].pProcessThreadInfo[nThreads].ClientId.UniqueThread
  6882. == ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueThread){
  6883. PMODULE module = FindModuleForAddress64(ProcToMonitor, (DWORD64)ProcToMonitor->pProcThreadInfoStart[k].StartAddress);
  6884. FPRINTF(stdout, "\nPid= %I64d, Tid= %I64d, StartAddr= 0x%p",
  6885. (DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueProcess,
  6886. (DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueThread,
  6887. ProcToMonitor->pProcThreadInfoStart[k].StartAddress
  6888. );
  6889. if(module != NULL){
  6890. FPRINTF(stdout, " (%s)\n", module->module_Name);
  6891. }else{
  6892. FPRINTF(stdout, " (unknown module)\n");
  6893. }
  6894. FPRINTF(stdout, " Thread State , %15s, %15s\n",
  6895. ThreadState[ProcToMonitor->pProcThreadInfoStart[k].ThreadState],
  6896. ThreadState[pTask[m].pProcessThreadInfo[nThreads].ThreadState]
  6897. );
  6898. FPRINTF(stdout, " Wait Reason , %15s, %15s\n",
  6899. WaitReason[ProcToMonitor->pProcThreadInfoStart[k].WaitReason],
  6900. WaitReason[pTask[m].pProcessThreadInfo[nThreads].WaitReason]
  6901. );
  6902. OutputStartStopValues( ProcToMonitor->pProcThreadInfoStart[k].WaitTime,
  6903. pTask[m].pProcessThreadInfo[nThreads].WaitTime,
  6904. "Wait Time [.1 uSec]"
  6905. );
  6906. FPRINTF(stdout, " Base Priority , %15d, %15d\n",
  6907. ProcToMonitor->pProcThreadInfoStart[k].BasePriority,
  6908. pTask[m].pProcessThreadInfo[nThreads].BasePriority
  6909. );
  6910. FPRINTF(stdout, " Priority , %15d, %15d\n",
  6911. ProcToMonitor->pProcThreadInfoStart[k].Priority,
  6912. pTask[m].pProcessThreadInfo[nThreads].Priority
  6913. );
  6914. OutputStartStopValues( ProcToMonitor->pProcThreadInfoStart[k].ContextSwitches,
  6915. pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
  6916. "Context Switches"
  6917. );
  6918. Diff = pTask[m].pProcessThreadInfo[nThreads].ContextSwitches
  6919. - ProcToMonitor->pProcThreadInfoStart[k].ContextSwitches;
  6920. DisplayTotalAndRate( ProcToMonitor->pProcThreadInfoStart[k].ContextSwitches,
  6921. pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
  6922. gldElapsedSeconds,
  6923. "Context Switches",
  6924. "sec."
  6925. );
  6926. FPRINTF(stdout, "\n");
  6927. OutputPercentValue( ProcToMonitor->pProcThreadInfoStart[k].UserTime.QuadPart,
  6928. pTask[m].pProcessThreadInfo[nThreads].UserTime.QuadPart,
  6929. gTotal2ElapsedTime64,
  6930. "User Time"
  6931. );
  6932. OutputPercentValue( ProcToMonitor->pProcThreadInfoStart[k].KernelTime.QuadPart,
  6933. pTask[m].pProcessThreadInfo[nThreads].KernelTime.QuadPart,
  6934. gTotal2ElapsedTime64,
  6935. "Kernel Time"
  6936. );
  6937. Index[k] = TRUE;
  6938. bFound = TRUE;
  6939. break;
  6940. }
  6941. }
  6942. if(!bFound){ // This is a new thread
  6943. PMODULE module = FindModuleForAddress64(ProcToMonitor, (DWORD64)pTask[m].pProcessThreadInfo[nThreads].StartAddress);
  6944. FPRINTF(stdout, "\nPid= %I64d, Tid= %I64d, StartAddr= 0x%p",
  6945. (DWORD64)(DWORD64 *)pTask[m].pProcessThreadInfo[nThreads].ClientId.UniqueProcess,
  6946. (DWORD64)(DWORD64 *)pTask[m].pProcessThreadInfo[nThreads].ClientId.UniqueThread,
  6947. pTask[m].pProcessThreadInfo[nThreads].StartAddress
  6948. );
  6949. if(module != NULL){
  6950. FPRINTF(stdout, " (%s) --->(NEW)\n", module->module_Name);
  6951. }else{
  6952. FPRINTF(stdout, " (unknown module) --->(NEW)\n");
  6953. }
  6954. FPRINTF(stdout, " Thread State , %15s\n",
  6955. ThreadState[pTask[m].pProcessThreadInfo[nThreads].ThreadState]
  6956. );
  6957. FPRINTF(stdout, " Wait Reason , %15s\n",
  6958. WaitReason[pTask[m].pProcessThreadInfo[nThreads].WaitReason]
  6959. );
  6960. OutputStartStopValues( 0,
  6961. pTask[m].pProcessThreadInfo[nThreads].WaitTime,
  6962. "Wait Time [.1 uSec]"
  6963. );
  6964. FPRINTF(stdout, " Base Priority , %15d\n",
  6965. pTask[m].pProcessThreadInfo[nThreads].BasePriority
  6966. );
  6967. FPRINTF(stdout, " Priority , %15d\n",
  6968. pTask[m].pProcessThreadInfo[nThreads].Priority
  6969. );
  6970. OutputStartStopValues( 0,
  6971. pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
  6972. "Context Switches"
  6973. );
  6974. Diff = pTask[m].pProcessThreadInfo[nThreads].ContextSwitches;
  6975. DisplayTotalAndRate( 0,
  6976. pTask[m].pProcessThreadInfo[nThreads].ContextSwitches,
  6977. gldElapsedSeconds,
  6978. "Context Switches",
  6979. "sec."
  6980. );
  6981. FPRINTF(stdout, "\n");
  6982. OutputPercentValue( 0,
  6983. pTask[m].pProcessThreadInfo[nThreads].UserTime.QuadPart,
  6984. gTotal2ElapsedTime64,
  6985. "User Time"
  6986. );
  6987. OutputPercentValue( 0,
  6988. pTask[m].pProcessThreadInfo[nThreads].KernelTime.QuadPart,
  6989. gTotal2ElapsedTime64,
  6990. "Kernel Time"
  6991. );
  6992. }
  6993. }
  6994. //
  6995. // Anything beyond this is a thread that is already GONE
  6996. //
  6997. k = ProcToMonitor->ProcPerfInfoStart.NumberOfThreads;
  6998. while (k--){
  6999. if(Index[k] == FALSE){
  7000. PMODULE module = FindModuleForAddress64(ProcToMonitor, (DWORD64)ProcToMonitor->pProcThreadInfoStart[k].StartAddress);
  7001. FPRINTF(stdout, "\nPid= %I64d, Tid= %I64d, StartAddr= 0x%p",
  7002. (DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueProcess,
  7003. (DWORD64)(DWORD64 *)ProcToMonitor->pProcThreadInfoStart[k].ClientId.UniqueThread,
  7004. ProcToMonitor->pProcThreadInfoStart[k].StartAddress
  7005. );
  7006. if(module != NULL){
  7007. FPRINTF(stdout, " (%s) --->(GONE)\n", module->module_Name);
  7008. }else{
  7009. FPRINTF(stdout, " (unknown module) --->(GONE)\n");
  7010. }
  7011. }
  7012. }
  7013. if(Index != NULL){
  7014. free(Index);
  7015. Index = NULL;
  7016. }
  7017. }//OutputThreadInfo()
  7018. VOID
  7019. DisplayRunningTasksSummary (
  7020. PTASK_LIST pTaskStart,
  7021. PTASK_LIST pTaskStop
  7022. )
  7023. /*++
  7024. Routine Description:
  7025. Displays a summary of all processes running at the start and at the end of Kernrate's run
  7026. and their average CPU utilization.
  7027. Processes that existed at the start count but not at the end count will be marked as "GONE".
  7028. Processes that exist at the end count but not at the start count will be marked as "NEW".
  7029. The list will miss short-lived processes that were created after the start count but went away before the end count.
  7030. Arguments:
  7031. pTaskStart - Pointer to the task list taken at the start
  7032. pTaskStart - Pointer to the task list taken at the end
  7033. Return Value:
  7034. None.
  7035. --*/
  7036. {
  7037. ULONG i, j;
  7038. BOOL bFound;
  7039. BOOL *Index = (BOOL *)calloc(gNumTasksStart, sizeof(BOOL));
  7040. if ( Index == NULL ){
  7041. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for Index in DisplayRunningTasksSummary\n");
  7042. exit(1);
  7043. }
  7044. FPRINTF(stdout, "Found %u processes at the start point, %u processes at the stop point\n",
  7045. gNumTasksStart,
  7046. gNumTasksStop
  7047. );
  7048. FPRINTF(stdout, "Percentage in the following table is based on the Elapsed Time\n");
  7049. FPRINTF(stdout, "\n ProcessID, Process Name, Kernel Time, User-Mode Time, Idle Time\n\n");
  7050. for (i=0; i < gNumTasksStop; i++) {
  7051. bFound = FALSE;
  7052. for (j=0; j < gNumTasksStart; j++) {
  7053. if ( pTaskStop[i].ProcessId == pTaskStart[j].ProcessId ) {
  7054. long double UserPercentValue;
  7055. long double KernelPercentValue;
  7056. LARGE_INTEGER Diff;
  7057. Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.KernelTime.QuadPart -
  7058. pTaskStart[j].ProcessPerfInfo.KernelTime.QuadPart;
  7059. KernelPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
  7060. Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.UserTime.QuadPart -
  7061. pTaskStart[j].ProcessPerfInfo.UserTime.QuadPart;
  7062. UserPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
  7063. // Additional Perf Data can be added if needed
  7064. if( pTaskStop[i].ProcessId != 0 ) {
  7065. FPRINTF(stdout, "%12I64d, %32s, %10.2f%%, %10.2f%%\n",
  7066. pTaskStop[i].ProcessId,
  7067. pTaskStop[i].ProcessName,
  7068. KernelPercentValue,
  7069. UserPercentValue
  7070. );
  7071. } else { // This is the System Idle Process
  7072. FPRINTF(stdout, "%12I64d, %32s, %10.2f%%, %10.2f%%, ~%6.2f%%\n",
  7073. pTaskStop[i].ProcessId,
  7074. pTaskStop[i].ProcessName,
  7075. KernelPercentValue,
  7076. 0.00,
  7077. KernelPercentValue
  7078. );
  7079. }
  7080. bFound = TRUE;
  7081. Index[j] = TRUE;
  7082. break;
  7083. }
  7084. }
  7085. //
  7086. // No match found in the START list, therefore this is a NEW process
  7087. //
  7088. if( !bFound ) {
  7089. long double UserPercentValue;
  7090. long double KernelPercentValue;
  7091. LARGE_INTEGER Diff;
  7092. Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.KernelTime.QuadPart;
  7093. KernelPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
  7094. Diff.QuadPart = pTaskStop[i].ProcessPerfInfo.UserTime.QuadPart;
  7095. UserPercentValue = gTotalElapsedSeconds > 0? 100*(long double)Diff.QuadPart/(long double)gTotal2ElapsedTime64.QuadPart : 0;
  7096. FPRINTF(stdout, "%12I64d, %32s, %10.2f%%, %10.2f%%, NEW\n",
  7097. pTaskStop[i].ProcessId,
  7098. pTaskStop[i].ProcessName,
  7099. KernelPercentValue,
  7100. UserPercentValue
  7101. );
  7102. }
  7103. }//for(i)
  7104. //
  7105. // No match found in the STOP list, therefore this process is GONE
  7106. //
  7107. for (j=0; j < gNumTasksStart; j++){
  7108. if( Index[j] == FALSE ) {
  7109. FPRINTF(stdout, "%12I64d, %32s, GONE\n",
  7110. pTaskStart[j].ProcessId,
  7111. pTaskStart[j].ProcessName
  7112. );
  7113. }
  7114. }
  7115. //
  7116. // cleanup
  7117. //
  7118. if(Index != NULL){
  7119. free(Index);
  7120. Index = NULL;
  7121. }
  7122. } //DisplayRunningTasksSummary()
  7123. PMODULE
  7124. FindModuleForAddress64(
  7125. PPROC_TO_MONITOR ProcToMonitor,
  7126. DWORD64 Address
  7127. )
  7128. {
  7129. PMODULE Module = ProcToMonitor->ModuleList;
  7130. while( Module != NULL ) {
  7131. if( ( (PVOID)Address >= (PVOID)Module->Base) && ( (PVOID)Address < (PVOID)(Module->Base + (ULONG64)Module->Length) ) ){
  7132. return (Module);
  7133. }
  7134. Module = Module->Next;
  7135. }
  7136. return (NULL);
  7137. } //FindModuleForAddress64()
  7138. VOID
  7139. OutputLineFromAddress64(
  7140. HANDLE hProc,
  7141. DWORD64 qwAddr,
  7142. PIMAGEHLP_LINE64 pLine
  7143. )
  7144. /*++
  7145. Routine Description:
  7146. Gets source-code line number and file information for a given address
  7147. and outputs the data
  7148. Arguments:
  7149. hProc - Imagehlp Handle to the process
  7150. qwAddr - Address for which we need the source code line information
  7151. pLine - Pointer to a user allocated IMAGEHLP_LINE64 struct
  7152. Return Value:
  7153. None.
  7154. Notes:
  7155. If successful, the data returned in pLine includes the source-code line number,
  7156. the full path to the source file, the displacement (not used here) and the address
  7157. where the first instruction is encountered in the line.
  7158. If unsuccessful, nothing is printed. In most cases this is due to an unmatching
  7159. symbol file or a supplied address that does not contain code instructions.
  7160. This can also happen if the pdb file does not contain source line information.
  7161. --*/
  7162. {
  7163. DWORD dwDisplacement = 0;
  7164. if ( SymGetLineFromAddr64( hProc, qwAddr, &dwDisplacement, pLine) ){
  7165. FPRINTF(stdout, " (line %ld in %s, 0x%I64x)",
  7166. pLine->LineNumber,
  7167. pLine->FileName,
  7168. pLine->Address
  7169. );
  7170. }
  7171. }//OutputLineFromAddress64
  7172. //MC
  7173. BOOL
  7174. InitializeManagedCodeSupport(
  7175. PPROC_TO_MONITOR ProcToMonitor
  7176. )
  7177. /*++
  7178. Routine Description:
  7179. If the main LKR library (mscoree.dll) is loaded, load the managed code helper (ip2md.dll) unless it's already loaded
  7180. and get pointers to its main function calls. If successful, get the JIT ranges for the current process.
  7181. If no JIT ranges are identified - return failure, but don't unload the helper library yet in case another process
  7182. needs it.
  7183. Arguments:
  7184. ProcToMonitor - Pointer to the structure of the process being monitored
  7185. Return Value:
  7186. TRUE for success, FALSE for Failure
  7187. --*/
  7188. {
  7189. BOOL bRet = FALSE;
  7190. // Managed code (CLR) currently not supported on IA64 - just return FALSE on initialization attempt
  7191. #if defined(_X86_)
  7192. int i,j;
  7193. if( bMCHelperLoaded != TRUE ){
  7194. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Found mscoree.dll, loading ip2md.dll Managed code helper\n"));
  7195. ghMCLib = LoadLibrary( MANAGED_CODE_SYMHLPLIB );
  7196. if(ghMCLib == NULL){
  7197. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to load Managed Code helper library (ip2md.dll)\n"));
  7198. }else{
  7199. bMCHelperLoaded = TRUE;
  7200. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Succsessfully loaded ip2md.dll Managed code helper\n"));
  7201. pfnAttachToProcess = (PFN1)GetProcAddress(ghMCLib, "AttachToProcess");
  7202. if(!pfnAttachToProcess){
  7203. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnAttachToProcess from ip2md.dll\n"));
  7204. bMCHelperLoaded = FALSE;
  7205. }
  7206. pfnDetachFromProcess = (PFN2)GetProcAddress(ghMCLib, "DetachFromProcess");
  7207. if(!pfnDetachFromProcess){
  7208. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnDetachFromProcess from ip2md.dll\n"));
  7209. bMCHelperLoaded = FALSE;
  7210. }
  7211. pfnIP2MD = (PFN3)GetProcAddress(ghMCLib, "IP2MD");
  7212. if(!pfnIP2MD){
  7213. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnIP2MD from ip2md.dll\n"));
  7214. bMCHelperLoaded = FALSE;
  7215. }
  7216. pfnGetJitRange = (PFN4)GetProcAddress(ghMCLib, "GetJitRange");
  7217. if(!pfnGetJitRange){
  7218. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Failed to get proc address for pfnGetJitRange from ip2md.dll\n"));
  7219. bMCHelperLoaded = FALSE;
  7220. }
  7221. if( bMCHelperLoaded == FALSE && ghMCLib != NULL){
  7222. FreeLibrary(ghMCLib);
  7223. ghMCLib = NULL;
  7224. }
  7225. }
  7226. }
  7227. if( bMCHelperLoaded == TRUE ){
  7228. //
  7229. //Initialization will be considered successful at this point even if there are no JIT ranges
  7230. //We'll keep the helper library loaded because there may be some Pre-Generated modules loaded as modules in the process
  7231. //
  7232. bRet = TRUE;
  7233. VerbosePrint((VERBOSE_INTERNALS, "KERNRATE: Attaching to Process %I64d to get JIT ranges\n", ProcToMonitor->Pid));
  7234. pfnAttachToProcess( (DWORD)ProcToMonitor->Pid );
  7235. ProcToMonitor->JITHeapLocationsStart = pfnGetJitRange();
  7236. if( ProcToMonitor->JITHeapLocationsStart == NULL){
  7237. VerbosePrint((VERBOSE_INTERNALS,
  7238. "KERNRATE: No JIT heap locations returned for Pid= %I64d, detaching from process\n",
  7239. ProcToMonitor->Pid
  7240. ));
  7241. } else {
  7242. if(gVerbose & VERBOSE_INTERNALS){
  7243. FPRINTF(stderr, "KERNRATE: Identified JIT ranges (Name, BaseAddr, Length:\n");
  7244. i = 0;
  7245. j = 0;
  7246. while( ProcToMonitor->JITHeapLocationsStart[i] != 0 ){
  7247. FPRINTF(stderr, "JIT%d, 0x%lx, %ld\n",
  7248. j,
  7249. ProcToMonitor->JITHeapLocationsStart[i],
  7250. ProcToMonitor->JITHeapLocationsStart[i+1]
  7251. );
  7252. i+=2;
  7253. j+=1;
  7254. }
  7255. }
  7256. bMCJitRangesExist = TRUE;
  7257. }
  7258. pfnDetachFromProcess();
  7259. }
  7260. #endif //defined(_X86_)
  7261. return (bRet);
  7262. } //InitializeManagedCodeSupport()
  7263. VOID
  7264. OutputJITRangeComparison(
  7265. PPROC_TO_MONITOR ProcToMonitor
  7266. )
  7267. /*++
  7268. Routine Description:
  7269. Compares the lists of a process JIT ranges before and after the profiling.
  7270. The list will miss short-lived JIT ranges that were created after the start count but went away before the end count.
  7271. Arguments:
  7272. ProcToMonitor - Pointer to the structure of the process being monitored
  7273. Return Value:
  7274. None.
  7275. --*/
  7276. {
  7277. ULONG i,j, jcount;
  7278. BOOL bFound;
  7279. BOOL *index;
  7280. // We already checked if ProcToMonitor->JITHeapLocationsStart != NULL before calling
  7281. jcount = 0;
  7282. if( ProcToMonitor->JITHeapLocationsStop != NULL){
  7283. while ( ProcToMonitor->JITHeapLocationsStop[jcount] ){
  7284. jcount += 2;
  7285. }
  7286. }
  7287. index = (BOOL *)calloc( jcount, sizeof(BOOL) );
  7288. if( index == NULL ){
  7289. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for index array in OutputJITRangeComparison\n");
  7290. return;
  7291. }
  7292. i = 0;
  7293. FPRINTF(stdout, "\n---------------------- JIT RANGES STATUS AFTER DATA PROCESSING ------------------------\n\n");
  7294. FPRINTF(stdout, " Base-Address, Size, Status \n");
  7295. while ( ProcToMonitor->JITHeapLocationsStart[i] ){
  7296. j = 0;
  7297. bFound = FALSE;
  7298. if(ProcToMonitor->JITHeapLocationsStop != NULL){
  7299. while ( ProcToMonitor->JITHeapLocationsStop[j] ){
  7300. if( (ProcToMonitor->JITHeapLocationsStart[i] == ProcToMonitor->JITHeapLocationsStop[j]) &&
  7301. (ProcToMonitor->JITHeapLocationsStart[i+1] == ProcToMonitor->JITHeapLocationsStop[j+1]) ){
  7302. FPRINTF(stdout, "0x%lx %12ld EXISTS\n",
  7303. ProcToMonitor->JITHeapLocationsStart[i],
  7304. ProcToMonitor->JITHeapLocationsStart[i+1]
  7305. );
  7306. bFound = TRUE;
  7307. index[j] = TRUE;
  7308. break;
  7309. }else{
  7310. j += 2;
  7311. }
  7312. }
  7313. }
  7314. if(!bFound){
  7315. FPRINTF(stdout, "0x%lx %12ld GONE\n",
  7316. ProcToMonitor->JITHeapLocationsStart[i],
  7317. ProcToMonitor->JITHeapLocationsStart[i+1]
  7318. );
  7319. }
  7320. i += 2;
  7321. }
  7322. if( ProcToMonitor->JITHeapLocationsStop != NULL){
  7323. j = 0;
  7324. while ( ProcToMonitor->JITHeapLocationsStop[j] ){
  7325. if( index[j] == FALSE ){
  7326. FPRINTF(stdout, "0x%lx %12ld NEW\n",
  7327. ProcToMonitor->JITHeapLocationsStop[j],
  7328. ProcToMonitor->JITHeapLocationsStop[j+1]
  7329. );
  7330. }
  7331. j += 2;
  7332. }
  7333. }
  7334. if( index != NULL ){
  7335. free(index);
  7336. index = NULL;
  7337. }
  7338. FPRINTF(stdout, "\n-----------------------------------------------------------\n\n");
  7339. } //OutputJITRangeComparison()
  7340. //MC
  7341. VOID
  7342. OutputRawDataForZoomModule(
  7343. IN FILE *Out,
  7344. IN PPROC_TO_MONITOR ProcToMonitor,
  7345. IN PMODULE Current
  7346. )
  7347. /*++
  7348. Routine Description:
  7349. Outputs raw profile data for every bucket in a zoom module
  7350. Arguments:
  7351. Out - Supplies the file pointer to output to.
  7352. ProcToMonitor - Pointer to the struct of the current process.
  7353. Current - Pointer to the current zoom module struct.
  7354. Return Value:
  7355. None.
  7356. Notes:
  7357. The routine will also try to find other sharers of the same bucket
  7358. If the routine fails to find a symbol for an address it will try
  7359. to find a Managed Code JIT method at that address, provided that
  7360. proper Managed Code support exists
  7361. --*/
  7362. {
  7363. PRATE_DATA RateData;
  7364. LONG CpuNumber;
  7365. ULONG i, ProfileSourceIndex, Index;
  7366. //MC
  7367. ULONGLONG TempTotal, TotCount, TotUnknownSymbolCount, TotJITCount, TotPreJITCount;
  7368. //MC
  7369. CHAR LastSymbol[cMODULE_NAME_STRLEN];
  7370. HANDLE SymHandle = ProcToMonitor->ProcessHandle;
  7371. BOOL bLastSymbolExists;
  7372. BOOL bAttachedToProcess = FALSE;
  7373. BOOL bPrintLastTotal = FALSE;
  7374. BOOL bPrintJITLastTotal = FALSE;
  7375. BOOL bPrintPREJITLastTotal = FALSE;
  7376. PIMAGEHLP_LINE64 pLine = malloc(sizeof(IMAGEHLP_LINE64));
  7377. if (pLine == NULL) {
  7378. FPRINTF(stderr, "KERNRATE: Buffer allocation failed for pLine in OutputResults\n");
  7379. exit(1);
  7380. }
  7381. pLine->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  7382. for (Index=0; Index < gTotalActiveSources; Index++) {
  7383. ProfileSourceIndex = gulActiveSources[Index];
  7384. FPRINTF(Out,
  7385. "\n---- RAW %s Profile: Source= %s, Module Base= 0x%I64x\n",
  7386. Current->module_Name,
  7387. ProcToMonitor->Source[ProfileSourceIndex].Name,
  7388. Current->Base
  7389. );
  7390. FPRINTF(Out, "Function Name+Displacement[Address], Bucket Number, Total Bucket Hits");
  7391. if (bProfileByProcessor) {
  7392. FPRINTF(Out, ", Hits on Processor(s)");
  7393. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  7394. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  7395. FPRINTF(Out," %2d,", CpuNumber);
  7396. }
  7397. }
  7398. FPRINTF(Out, " Source-Line, Source-File, Addr. of First Instruction\n\n");
  7399. TotCount = 0;
  7400. TotUnknownSymbolCount = 0;
  7401. //MC
  7402. TotJITCount = 0;
  7403. TotPreJITCount = 0;
  7404. //MC
  7405. bLastSymbolExists = FALSE;
  7406. RateData = &Current->Rate[ProfileSourceIndex];
  7407. for (i=0; i < BUCKETS_NEEDED(Current->Length) ; i++) {
  7408. TempTotal = 0;
  7409. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  7410. TempTotal += RateData->ProfileBuffer[CpuNumber][i];
  7411. }
  7412. if ( TempTotal > 0 ) {
  7413. // TF - FIXFIX - 07/2000.
  7414. // The current version of kernrate and kernel profiling objects code
  7415. // assume code section < 4GB.
  7416. //
  7417. ULONG64 ip = Current->Base + (ULONG64)(i * gZoomBucket);
  7418. ULONG64 disp = 0;
  7419. if ( !SymGetSymFromAddr64(SymHandle, ip, &disp, gSymbol ) ) {
  7420. //MC
  7421. //
  7422. // No Symbol, Let's check if this is a managed code address
  7423. //
  7424. int Retval = 0;
  7425. ULONG64 ipMax = ip + (ULONG64)gZoomBucket;
  7426. if( bMCHelperLoaded == TRUE) {
  7427. if( !bAttachedToProcess ){
  7428. pfnAttachToProcess((DWORD)ProcToMonitor->Pid);
  7429. bAttachedToProcess = TRUE;
  7430. }
  7431. //
  7432. // Try to find a Managed Code symbol anywhere in the bucket
  7433. //
  7434. while (Retval == 0 && ip < ipMax){
  7435. Retval = pfnIP2MD((DWORD_PTR)ip, &gwszSymbol);
  7436. ip += 1;
  7437. if( Retval > 0 && gwszSymbol == NULL ){
  7438. Retval = 0;
  7439. continue;
  7440. }
  7441. }
  7442. if( Retval > 0 && gwszSymbol != NULL ){
  7443. if( Retval == 1 ){
  7444. FPRINTF(Out, "%S[0x%I64x], %10Ld, %10Ld, JIT_TYPE",
  7445. gwszSymbol,
  7446. ip,
  7447. i,
  7448. TempTotal
  7449. );
  7450. TotJITCount += TempTotal;
  7451. }
  7452. else if(Retval == 2){
  7453. FPRINTF(Out, "%S[0x%I64x], %10Ld, %10Ld, PRE-JITTED_TYPE",
  7454. gwszSymbol,
  7455. ip,
  7456. i,
  7457. TempTotal
  7458. );
  7459. TotPreJITCount += TempTotal;
  7460. }
  7461. }
  7462. //
  7463. // Print other symbols in this bucket by incrementing
  7464. // by 2 bytes at a time. Note that we start from the ip reached above
  7465. // so the loop below may not get executed at all
  7466. //
  7467. ip += (ULONG64)2;
  7468. bPrintJITLastTotal = TRUE;
  7469. bPrintPREJITLastTotal = TRUE;
  7470. while( ip < ipMax ) {
  7471. WCHAR lastSym[cMODULE_NAME_STRLEN] = L"";
  7472. int retv = 0;
  7473. if(gwszSymbol != NULL){
  7474. wcsncpy( lastSym, gwszSymbol, cMODULE_NAME_STRLEN-1 );
  7475. _wcsset(&lastSym[ cMODULE_NAME_STRLEN-1 ], L'\0');
  7476. }
  7477. retv = pfnIP2MD( (DWORD_PTR)ip, &gwszSymbol );
  7478. if ( retv > 0 && gwszSymbol != NULL ) {
  7479. if ( wcscmp( lastSym, gwszSymbol ) ) {
  7480. if(retv == 1){
  7481. FPRINTF(Out, "JIT Module Total Count = %Ld\n\n", TotJITCount);
  7482. TotJITCount = 0;
  7483. }
  7484. else if(retv == 2){
  7485. FPRINTF(Out, "PRE-JITTED Module Total Count = %Ld\n\n", TotPreJITCount);
  7486. TotPreJITCount = 0;
  7487. }
  7488. FPRINTF( Out, " %S[0x%I64x]\n", gwszSymbol, ip );
  7489. wcsncpy( lastSym, gwszSymbol , cMODULE_NAME_STRLEN-1);
  7490. _wcsset( &lastSym[ cMODULE_NAME_STRLEN-1 ], L'\0');
  7491. bPrintJITLastTotal = FALSE;
  7492. bPrintPREJITLastTotal = FALSE;
  7493. }
  7494. }
  7495. ip += (ULONG64)2;
  7496. } // End of while( ip < ipMax )
  7497. } // if( bMCHelperLoaded )
  7498. //MC
  7499. //
  7500. // No Symbol and no managed code symbol found
  7501. //
  7502. if(!Retval){
  7503. FPRINTF(Out, "Unknown_Symbol+0x%I64x[0x%I64x], %10Ld, %10Ld",
  7504. disp,
  7505. ip,
  7506. i,
  7507. TempTotal
  7508. );
  7509. TotUnknownSymbolCount += TempTotal;
  7510. }
  7511. if (bProfileByProcessor) {
  7512. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  7513. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  7514. FPRINTF( Out,", %10Ld",
  7515. RateData->ProfileBuffer[CpuNumber][i]
  7516. );
  7517. }
  7518. }
  7519. FPRINTF(Out,"\n");
  7520. }
  7521. else {
  7522. CHAR symName[cMODULE_NAME_STRLEN];
  7523. ULONG64 ipMax;
  7524. if (i > 0 && strcmp( LastSymbol, gSymbol->Name ) ) {
  7525. if ( bLastSymbolExists )
  7526. FPRINTF(Out, "Module Total Count = %Ld\n\n", TotCount);
  7527. TotCount = 0;
  7528. strncpy(LastSymbol, gSymbol->Name, cMODULE_NAME_STRLEN-1);
  7529. LastSymbol[ cMODULE_NAME_STRLEN-1 ] = '\0';
  7530. bLastSymbolExists = TRUE;
  7531. }
  7532. TotCount += TempTotal;
  7533. _snprintf( symName, cMODULE_NAME_STRLEN*sizeof(CHAR)-1, "%s+0x%I64x[0x%I64x]", gSymbol->Name, disp, ip );
  7534. symName[cMODULE_NAME_STRLEN-1] = '\0';
  7535. if ( bRawDisasm ) {
  7536. CHAR sourceCode[528];
  7537. #ifndef DISASM_AVAILABLE
  7538. // Thierry - FIXFIX - 07/2000.
  7539. // dbg is not helping... The old disassembly APIs no longer work.
  7540. // I have to re-write a full disassembly wrapper.
  7541. #define Disasm( IpAddr, Buffer, Flag ) 0
  7542. #endif // !DISASM_AVAILABLE
  7543. if ( Disasm( &ip, sourceCode, FALSE ) ) {
  7544. FPRINTF( Out,"%-40s, %10Ld, %10Ld, %s",
  7545. symName,
  7546. i,
  7547. TempTotal,
  7548. sourceCode
  7549. );
  7550. if (bProfileByProcessor) {
  7551. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  7552. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  7553. FPRINTF( Out,", %10Ld",
  7554. RateData->ProfileBuffer[CpuNumber][i]
  7555. );
  7556. }
  7557. }
  7558. FPRINTF(Out,"\n");
  7559. }
  7560. else {
  7561. FPRINTF( Out,"%-40s, %10Ld, %10Ld<disasm:?????>",
  7562. symName,
  7563. i,
  7564. TempTotal
  7565. );
  7566. if (bProfileByProcessor) {
  7567. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  7568. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  7569. FPRINTF( Out,", %10Ld",
  7570. RateData->ProfileBuffer[CpuNumber][i]
  7571. );
  7572. }
  7573. }
  7574. FPRINTF(Out,"\n");
  7575. }
  7576. }
  7577. else {
  7578. FPRINTF( Out,"%-40s, %10Ld, %10Ld",
  7579. symName,
  7580. i,
  7581. TempTotal
  7582. );
  7583. if (bProfileByProcessor) {
  7584. for (CpuNumber=0; CpuNumber < gProfileProcessors; CpuNumber++) {
  7585. if( gAffinityMask && !((1<<CpuNumber) & gAffinityMask) )continue;
  7586. FPRINTF( Out,", %10Ld",
  7587. RateData->ProfileBuffer[CpuNumber][i]
  7588. );
  7589. }
  7590. }
  7591. OutputLineFromAddress64( SymHandle, ip, pLine);
  7592. FPRINTF(Out,"\n");
  7593. } //if(bRawDisasm)
  7594. //
  7595. // Print other symbols in this bucket by incrementing
  7596. // by 2 bytes at a time.
  7597. //
  7598. ipMax = ip + (ULONG64)gZoomBucket;
  7599. ip += (ULONG64)2;
  7600. bPrintLastTotal = TRUE;
  7601. while( ip < ipMax ) {
  7602. CHAR lastSym[cMODULE_NAME_STRLEN];
  7603. strncpy( lastSym, gSymbol->Name, cMODULE_NAME_STRLEN-1 );
  7604. lastSym[ cMODULE_NAME_STRLEN-1 ] = '\0';
  7605. if ( SymGetSymFromAddr64(SymHandle, ip, &disp , gSymbol ) ) {
  7606. if ( strcmp( lastSym, gSymbol->Name ) ) {
  7607. FPRINTF(Out, "Module Total Count = %Ld\n\n", TotCount);
  7608. TotCount = 0;
  7609. FPRINTF( Out, " %s+0x%I64x[0x%I64x] ", gSymbol->Name, disp, ip );
  7610. OutputLineFromAddress64( SymHandle, ip, pLine);
  7611. FPRINTF(stdout, "\n");
  7612. strncpy( lastSym, gSymbol->Name , cMODULE_NAME_STRLEN-1);
  7613. lastSym[ cMODULE_NAME_STRLEN-1 ] = '\0';
  7614. bPrintLastTotal = FALSE;
  7615. }
  7616. }
  7617. ip += (ULONG64)2;
  7618. } // End of while( ip < ipMax )
  7619. } //If(SymGetSymFromAddr64)
  7620. } //if(TempTotal > 0)
  7621. } // i bucket loop
  7622. //MC
  7623. if ( bPrintLastTotal == TRUE )
  7624. FPRINTF(Out, "Module Total Count (not including JIT, PRE-JIT and Unknown) = %Ld\n\n", TotCount);
  7625. if ( TotUnknownSymbolCount > 0 )
  7626. FPRINTF(Out, "Module Total Count For Unknown Symbols = %Ld\n\n", TotUnknownSymbolCount);
  7627. if( (TotJITCount > 0) && (bPrintJITLastTotal == TRUE) )
  7628. FPRINTF(Out, "Module Total Count For JIT type = %Ld\n\n", TotJITCount);
  7629. if( (TotPreJITCount > 0) && (bPrintPREJITLastTotal == TRUE) )
  7630. FPRINTF(Out, "Module Total Count For PRE-JITTED type = %Ld\n\n", TotPreJITCount);
  7631. //MC
  7632. } // Sources loop
  7633. if( pLine != NULL ){
  7634. free(pLine);
  7635. pLine = NULL;
  7636. }
  7637. }//OutputRawDataForZoomModule()
  7638. PSYSTEM_BASIC_INFORMATION
  7639. GetSystemBasicInformation(
  7640. VOID
  7641. )
  7642. {
  7643. NTSTATUS status;
  7644. PSYSTEM_BASIC_INFORMATION SystemInformation = NULL;
  7645. SystemInformation = malloc(sizeof(SYSTEM_BASIC_INFORMATION));
  7646. if(SystemInformation == NULL){
  7647. FPRINTF(stderr, "Buffer allocation failed for SystemInformation in GetSystemBasicInformation\n");
  7648. exit(1);
  7649. }
  7650. status = NtQuerySystemInformation(SystemBasicInformation,
  7651. SystemInformation,
  7652. sizeof(SYSTEM_BASIC_INFORMATION),
  7653. NULL);
  7654. if (!NT_SUCCESS(status)) {
  7655. FPRINTF(stderr, "NtQuerySystemInformation failed status %08lx\n",status);
  7656. if ( SystemInformation != NULL ){
  7657. free( SystemInformation );
  7658. SystemInformation = NULL;
  7659. }
  7660. }
  7661. return (SystemInformation);
  7662. }
  7663. VOID
  7664. InitializeKernrate(
  7665. int argc,
  7666. char *argv[]
  7667. )
  7668. {
  7669. ULONG j;
  7670. BOOL OSCompat = FALSE;
  7671. gOSInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO) ;
  7672. GetVersionEx(&gOSInfo);
  7673. if ( gOSInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ){
  7674. if ( gOSInfo.dwMajorVersion >= 5 ){
  7675. OSCompat = TRUE;
  7676. }
  7677. }
  7678. if ( OSCompat == FALSE ){
  7679. FPRINTF(stderr, "This version of Kernrate will only run on Windows 2000 or above\n");
  7680. exit(1);
  7681. }
  7682. //
  7683. // Initialize gSourceMaximum, find supported sources
  7684. // The dummy process will be used later to store user-defined event rates with the -i command line option
  7685. //
  7686. gMaxSimultaneousSources = MAX_SIMULTANEOUS_SOURCES;
  7687. gpProcDummy = calloc(1, sizeof(PROC_TO_MONITOR));
  7688. if (gpProcDummy==NULL) {
  7689. FPRINTF(stderr, "KERNRATE: Allocation for Process failed for the -I or -L options\n");
  7690. exit(1);
  7691. }
  7692. gSourceMaximum = InitializeProfileSourceInfo(gpProcDummy);
  7693. if ( gSourceMaximum == 0 || gStaticCount == 0 ) {
  7694. FPRINTF( stderr, "KERNRATE: no profile source detected for this machine...\n" );
  7695. exit(0);
  7696. }
  7697. // This will guaranty that we'll not enable any unwanted profiling
  7698. for (j=0; j < gSourceMaximum; j++) {
  7699. if( j < gStaticCount ) {
  7700. gpProcDummy->Source[j].Interval = gStaticSource[j].Interval;
  7701. if( j == SOURCE_TIME && gStaticSource[j].Interval == 0 )
  7702. gpProcDummy->Source[j].Interval = KRATE_DEFAULT_TIMESOURCE_RATE; //IA64 default is zero
  7703. } else {
  7704. gpProcDummy->Source[j].Interval = 0;
  7705. }
  7706. }
  7707. //
  7708. // Get the default IMAGEHLP global option mask
  7709. // NOTE: This global variable could be changed by GetConfigurations().
  7710. // It is required to initialize it before calling this function.
  7711. //
  7712. gSymOptions = SymGetOptions();
  7713. if ( gVerbose & VERBOSE_IMAGEHLP ) {
  7714. FPRINTF( stderr, "KERNRATE: default IMAGEHLP SymOptions: %s\n", GetSymOptionsValues( gSymOptions ) );
  7715. }
  7716. //Bump kernrate's security privileges to debugger priveleges for tough guys like csrss.exe and dllhost.exe
  7717. if (!InitializeAsDebugger()){
  7718. FPRINTF(stderr, "KERNRATE: Unable to Get Debugger Security Privileges\n");
  7719. exit(1);
  7720. }
  7721. //
  7722. // Get some basic info about the system, determine the number of processors
  7723. //
  7724. gSysBasicInfo = GetSystemBasicInformation();
  7725. if( gSysBasicInfo == NULL){
  7726. FPRINTF(stderr, "KERNRATE: Failed to get SYSTEM_BASIC_INFORMATION\n");
  7727. exit(1);
  7728. }
  7729. //
  7730. // Get initial parameters from the Command Line
  7731. //
  7732. GetConfiguration(argc, argv);
  7733. //
  7734. // Initialize dbghelp
  7735. //
  7736. // Note that gSymOptions could have been modified in GetConfiguration().
  7737. //
  7738. SymSetOptions( gSymOptions );
  7739. if ( gVerbose & VERBOSE_IMAGEHLP ) {
  7740. FPRINTF( stderr, "KERNRATE: current IMAGEHLP SymOptions: %s\n", GetSymOptionsValues( gSymOptions ) );
  7741. }
  7742. gSymbol = (PIMAGEHLP_SYMBOL64) malloc( sizeof(IMAGEHLP_SYMBOL64) + MAX_SYMNAME_SIZE );
  7743. if ( gSymbol == NULL ) {
  7744. FPRINTF(stderr, "KERNRATE: Allocation for gSymbol failed\n");
  7745. exit(1);
  7746. }
  7747. gSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
  7748. gSymbol->MaxNameLength = MAX_SYMNAME_SIZE;
  7749. }//InitializeKernrate()
  7750. VOID
  7751. InitAllProcessesModulesInfo(
  7752. VOID
  7753. )
  7754. {
  7755. PPROC_TO_MONITOR ProcToMonitor = NULL;
  7756. PMODULE ZoomModule;
  7757. ULONG i, j;
  7758. //
  7759. // Deal with Kernel Profile Initialization
  7760. //
  7761. if(gNumProcToMonitor == 0 || bCombinedProfile == TRUE ){
  7762. if( !InitializeKernelProfile()){
  7763. FPRINTF(stderr, "Failed to Initialize Kernel Profile\n");
  7764. exit(1);
  7765. }
  7766. }
  7767. //
  7768. // Deal with per-Process Profile Initialization
  7769. //
  7770. // Get information on kernel and/or process modules
  7771. //
  7772. ProcToMonitor = gProcessList;
  7773. for (i=0; i < gNumProcToMonitor; i++){
  7774. if(ProcToMonitor->ProcessHandle != SYM_KERNEL_HANDLE){
  7775. if ( gVerbose & VERBOSE_IMAGEHLP ) {
  7776. FPRINTF(stdout, "ProcessID= %I64d\n", ProcToMonitor->Pid);
  7777. }
  7778. SymInitialize( ProcToMonitor->ProcessHandle, NULL, FALSE );
  7779. if(!bSymPathInitialized) {
  7780. InitSymbolPath( ProcToMonitor->ProcessHandle );
  7781. }
  7782. else {
  7783. SymSetSearchPath(ProcToMonitor->ProcessHandle, gSymbolPath);
  7784. }
  7785. if ( SymRegisterCallback64( ProcToMonitor->ProcessHandle, SymbolCallbackFunction, (ULONG64)&gCurrentModule ) != TRUE ) {
  7786. FPRINTF( stderr, "KERNRATE: Failed to register callback for IMAGEHLP process-handle operations...\n" );
  7787. }
  7788. ProcToMonitor->ModuleList = GetProcessModuleInformation(ProcToMonitor);
  7789. if(ProcToMonitor->ModuleList != NULL)
  7790. ProcToMonitor->ModuleList->mu.bProfileStarted = FALSE;
  7791. for(j=0; j<gSourceMaximum; j++){
  7792. ProcToMonitor->Source[j].bProfileStarted = FALSE;
  7793. }
  7794. }
  7795. else{
  7796. ProcToMonitor->ModuleList = GetKernelModuleInformation();
  7797. if(ProcToMonitor->ModuleList != NULL)
  7798. ProcToMonitor->ModuleList->mu.bProfileStarted = FALSE;
  7799. for(j=0; j < gSourceMaximum; j++){
  7800. ProcToMonitor->Source[j].bProfileStarted=FALSE;
  7801. }
  7802. }
  7803. //
  7804. // Any remaining entries on the ZoomList are liable to be errors.
  7805. //
  7806. if(gVerbose & VERBOSE_MODULES){
  7807. ZoomModule = ProcToMonitor->ZoomList;
  7808. while (ZoomModule != NULL) {
  7809. PMODULE Module = ProcToMonitor->ModuleList;
  7810. BOOL bFound = FALSE;
  7811. while (Module != NULL) {
  7812. if( 0 == _stricmp(ZoomModule->module_Name, Module->module_Name) ||
  7813. 0 == _stricmp(ZoomModule->module_Name, Module->module_FileName) ){
  7814. FPRINTF(stdout,
  7815. "===>KERNRATE: Requested zoom module %s was found in the process modules list\n",
  7816. ZoomModule->module_Name
  7817. );
  7818. bFound = TRUE;
  7819. break;
  7820. }
  7821. Module = Module->Next;
  7822. }
  7823. if(!bFound){
  7824. FPRINTF(stdout,
  7825. "===>KERNRATE: Requested zoom module %s was not found in the process modules list, ignoring\n",
  7826. ZoomModule->module_Name
  7827. );
  7828. }
  7829. ZoomModule = ZoomModule->Next;
  7830. }
  7831. }
  7832. CleanZoomModuleList(ProcToMonitor); //Done with the zoom list - cleanup for reuse in the post processing
  7833. ProcToMonitor = ProcToMonitor->Next;
  7834. } //for
  7835. }//InitAllProcessesModulesInfo
  7836. VOID
  7837. CleanZoomModuleList(
  7838. PPROC_TO_MONITOR Proc
  7839. )
  7840. {
  7841. PMODULE Temp = Proc->ZoomList;
  7842. while (Temp != NULL) {
  7843. Proc->ZoomList = Proc->ZoomList->Next;
  7844. free(Temp);
  7845. Temp = NULL;
  7846. Temp = Proc->ZoomList;
  7847. }
  7848. } // CleanZoomModuleList()
  7849. VOID
  7850. ExecuteProfiles(
  7851. BOOL bMode
  7852. )
  7853. {
  7854. ULONG i,j;
  7855. DWORD Error = ERROR_SUCCESS;
  7856. PPROC_TO_MONITOR ProcToMonitor = NULL;
  7857. ULONG ProcessActiveSource = 0;
  7858. ULONG LastActiveSource = 0;
  7859. if(bMode){
  7860. //old scheme - turn on one source at a time, switch sources every ChangeInterval ms
  7861. // Note that this will cause the total profile time to be devided equally
  7862. // between the N processes being monitored, each being profiled for
  7863. // an amount of TotalTime/N seconds...
  7864. do{
  7865. ProcToMonitor = gProcessList;
  7866. for (i=0; i<gNumProcToMonitor; i++){
  7867. if( ProcToMonitor->ModuleList != NULL ){
  7868. do{
  7869. LastActiveSource = ProcessActiveSource;
  7870. ProcessActiveSource = NextSource(ProcessActiveSource,
  7871. ProcToMonitor->ModuleList,
  7872. ProcToMonitor
  7873. );
  7874. ProcToMonitor->ModuleList->mu.bProfileStarted = TRUE;
  7875. Error = WaitForSingleObject(ghDoneEvent, gChangeInterval);
  7876. }while(ProcessActiveSource > LastActiveSource);
  7877. StopSource(ProcessActiveSource, ProcToMonitor->ModuleList, ProcToMonitor);
  7878. }
  7879. ProcToMonitor->Source[ProcessActiveSource].bProfileStarted = FALSE;
  7880. ProcToMonitor = ProcToMonitor->Next;
  7881. }
  7882. } while ( Error == WAIT_TIMEOUT );
  7883. } else { // new scheme - turn on all requested profile sources and let them run simultaneously
  7884. ProcToMonitor = gProcessList;
  7885. for (i=0; i<gNumProcToMonitor; i++){
  7886. if( ProcToMonitor->ModuleList != NULL ){
  7887. for (j=0; j < gTotalActiveSources; j++){
  7888. StartSource(gulActiveSources[j], ProcToMonitor->ModuleList, ProcToMonitor);
  7889. }
  7890. }
  7891. ProcToMonitor = ProcToMonitor->Next;
  7892. }
  7893. do{
  7894. Error = WaitForSingleObject(ghDoneEvent, gChangeInterval); //ChangeInterval does not switch any sources here
  7895. } while ( Error == WAIT_TIMEOUT );
  7896. ProcToMonitor = gProcessList;
  7897. for (i=0; i<gNumProcToMonitor; i++){
  7898. if( ProcToMonitor->ModuleList != NULL ){
  7899. for (j=0; j < gTotalActiveSources; j++){
  7900. StopSource(gulActiveSources[j], ProcToMonitor->ModuleList, ProcToMonitor);
  7901. }
  7902. }
  7903. ProcToMonitor = ProcToMonitor->Next;
  7904. }
  7905. } //if(bMode)
  7906. }//ExecuteProfiles()
  7907. VOID
  7908. DisplaySystemWideInformation(
  7909. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoBegin,
  7910. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInfoEnd
  7911. )
  7912. {
  7913. TIME_FIELDS Time;
  7914. ULONG i;
  7915. ULONG InterruptCounts, TotalInterruptCounts;
  7916. LARGE_INTEGER Elapsed, Idle, Kernel, User, Dpc, Interrupt;
  7917. LARGE_INTEGER TotalElapsed, TotalIdle, TotalKernel, TotalUser, TotalDpc, TotalInterrupt;
  7918. TotalElapsed.QuadPart = 0;
  7919. TotalIdle.QuadPart = 0;
  7920. TotalKernel.QuadPart = 0;
  7921. TotalUser.QuadPart = 0;
  7922. TotalDpc.QuadPart = 0;
  7923. TotalInterrupt.QuadPart = 0;
  7924. TotalInterruptCounts = 0;
  7925. FPRINTF(stdout, "\n------------Overall Summary:--------------\n\n");
  7926. for (i=0; i<(ULONG)gSysBasicInfo->NumberOfProcessors; i++) {
  7927. double n;
  7928. long double m;
  7929. Idle.QuadPart = SystemInfoEnd[i].IdleTime.QuadPart - SystemInfoBegin[i].IdleTime.QuadPart;
  7930. User.QuadPart = SystemInfoEnd[i].UserTime.QuadPart - SystemInfoBegin[i].UserTime.QuadPart;
  7931. Kernel.QuadPart = SystemInfoEnd[i].KernelTime.QuadPart - SystemInfoBegin[i].KernelTime.QuadPart;
  7932. Elapsed.QuadPart = Kernel.QuadPart + User.QuadPart;
  7933. Kernel.QuadPart -= Idle.QuadPart;
  7934. Dpc.QuadPart = SystemInfoEnd[i].DpcTime.QuadPart - SystemInfoBegin[i].DpcTime.QuadPart;
  7935. Interrupt.QuadPart = SystemInfoEnd[i].InterruptTime.QuadPart - SystemInfoBegin[i].InterruptTime.QuadPart;
  7936. InterruptCounts = SystemInfoEnd[i].InterruptCount - SystemInfoBegin[i].InterruptCount;
  7937. TotalKernel.QuadPart += Kernel.QuadPart;
  7938. TotalUser.QuadPart += User.QuadPart;
  7939. TotalIdle.QuadPart += Idle.QuadPart;
  7940. TotalElapsed.QuadPart += Elapsed.QuadPart;
  7941. TotalDpc.QuadPart += Dpc.QuadPart;
  7942. TotalInterrupt.QuadPart += Interrupt.QuadPart;
  7943. TotalInterruptCounts += InterruptCounts;
  7944. FPRINTF(stdout, "P%d ",i);
  7945. RtlTimeToTimeFields(&Kernel, &Time);
  7946. n = UInt64ToDoublePerCent( Kernel.QuadPart, Elapsed.QuadPart );
  7947. FPRINTF(stdout, " K %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  7948. Time.Hour,
  7949. Time.Minute,
  7950. Time.Second,
  7951. Time.Milliseconds,
  7952. n );
  7953. RtlTimeToTimeFields(&User, &Time);
  7954. n = UInt64ToDoublePerCent( User.QuadPart, Elapsed.QuadPart );
  7955. FPRINTF(stdout, " U %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  7956. Time.Hour,
  7957. Time.Minute,
  7958. Time.Second,
  7959. Time.Milliseconds,
  7960. n );
  7961. RtlTimeToTimeFields(&Idle, &Time);
  7962. n = UInt64ToDoublePerCent( Idle.QuadPart, Elapsed.QuadPart );
  7963. FPRINTF(stdout, " I %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  7964. Time.Hour,
  7965. Time.Minute,
  7966. Time.Second,
  7967. Time.Milliseconds,
  7968. n );
  7969. RtlTimeToTimeFields(&Dpc, &Time);
  7970. n = UInt64ToDoublePerCent( Dpc.QuadPart, Elapsed.QuadPart );
  7971. FPRINTF(stdout, " DPC %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  7972. Time.Hour,
  7973. Time.Minute,
  7974. Time.Second,
  7975. Time.Milliseconds,
  7976. n );
  7977. RtlTimeToTimeFields(&Interrupt, &Time);
  7978. n = UInt64ToDoublePerCent( Interrupt.QuadPart, Elapsed.QuadPart );
  7979. FPRINTF(stdout, " Interrupt %ld:%02ld:%02ld.%03ld (%4.1f%%)\n",
  7980. Time.Hour,
  7981. Time.Minute,
  7982. Time.Second,
  7983. Time.Milliseconds,
  7984. n );
  7985. m = Elapsed.QuadPart > 0 ? (long double)10000000 * (long double)InterruptCounts / (long double)Elapsed.QuadPart : 0;
  7986. FPRINTF(stdout, " Interrupts= %ld, Interrupt Rate= %.0f/sec.\n\n",
  7987. InterruptCounts,
  7988. m );
  7989. }
  7990. if (gSysBasicInfo->NumberOfProcessors > 1) {
  7991. double n;
  7992. long double m;
  7993. FPRINTF(stdout, "TOTAL");
  7994. RtlTimeToTimeFields(&TotalKernel, &Time);
  7995. n = UInt64ToDoublePerCent( TotalKernel.QuadPart, TotalElapsed.QuadPart );
  7996. FPRINTF(stdout, " K %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  7997. Time.Hour,
  7998. Time.Minute,
  7999. Time.Second,
  8000. Time.Milliseconds,
  8001. n );
  8002. RtlTimeToTimeFields(&TotalUser, &Time);
  8003. n = UInt64ToDoublePerCent( TotalUser.QuadPart, TotalElapsed.QuadPart );
  8004. FPRINTF(stdout, " U %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  8005. Time.Hour,
  8006. Time.Minute,
  8007. Time.Second,
  8008. Time.Milliseconds,
  8009. n );
  8010. RtlTimeToTimeFields(&TotalIdle, &Time);
  8011. n = UInt64ToDoublePerCent( TotalIdle.QuadPart, TotalElapsed.QuadPart );
  8012. gTotalIdleTime = n;
  8013. FPRINTF(stdout, " I %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  8014. Time.Hour,
  8015. Time.Minute,
  8016. Time.Second,
  8017. Time.Milliseconds,
  8018. n );
  8019. RtlTimeToTimeFields(&TotalDpc, &Time);
  8020. n = UInt64ToDoublePerCent( TotalDpc.QuadPart, TotalElapsed.QuadPart );
  8021. FPRINTF(stdout, " DPC %ld:%02ld:%02ld.%03ld (%4.1f%%)",
  8022. Time.Hour,
  8023. Time.Minute,
  8024. Time.Second,
  8025. Time.Milliseconds,
  8026. n );
  8027. RtlTimeToTimeFields(&TotalInterrupt, &Time);
  8028. n = UInt64ToDoublePerCent( TotalInterrupt.QuadPart, TotalElapsed.QuadPart );
  8029. FPRINTF(stdout, " Interrupt %ld:%02ld:%02ld.%03ld (%4.1f%%)\n",
  8030. Time.Hour,
  8031. Time.Minute,
  8032. Time.Second,
  8033. Time.Milliseconds,
  8034. n );
  8035. m = Elapsed.QuadPart > 0 ? (long double)10000000 * (long double)TotalInterruptCounts / (long double)Elapsed.QuadPart : 0;
  8036. FPRINTF(stdout, " Total Interrupts= %ld, Total Interrupt Rate= %.0f/sec.\n\n",
  8037. TotalInterruptCounts,
  8038. m );
  8039. }
  8040. //
  8041. // Display system-wide counters information
  8042. //
  8043. gldElapsedSeconds = (long double)Elapsed.QuadPart / (long double)10000000;
  8044. gTotalElapsedSeconds = (ULONG)(TotalElapsed.QuadPart/10000000); //Sum on all processors
  8045. gTotalElapsedTime64.QuadPart = TotalElapsed.QuadPart;
  8046. FPRINTF(stdout, "\nTotal Profile Time = %ld msec\n", (ULONG)Elapsed.QuadPart/(ULONG)10000);
  8047. if(bIncludeGeneralInfo)
  8048. GetProfileSystemInfo(OUTPUT);
  8049. if(bIncludeSystemLocksInfo)
  8050. GetSystemLocksInformation(OUTPUT);
  8051. }//DisplaySystemWideInformation()
  8052. BOOL
  8053. IsStringANumber(
  8054. IN PCHAR String
  8055. )
  8056. /*++
  8057. Routine Description:
  8058. Checks if a string is representing a positive decimal form integer
  8059. not larger than 999,999,999 (9 digits maximum)
  8060. Arguments:
  8061. String - Supplies the character string pointer.
  8062. Return Value:
  8063. TRUE - The string represents a number under the restrictions above
  8064. FALSE - The string does not represent a number under the restrictions above
  8065. Notes:
  8066. The functions atol() and atoi() will return a number for a mixed string that starts with a number,
  8067. such as 345d (345 will be returned). Also they don't handle overflow conditions.
  8068. --*/
  8069. {
  8070. BOOL bRet = FALSE;
  8071. ULONG i = 0;
  8072. if ( String != NULL ) {
  8073. while( (i <= MAX_DIGITS_IN_INPUT_STRING) && isdigit( String[i] ) ){
  8074. if( String[i+1] == ' ' || iscntrl( String[i+1] ) ){
  8075. bRet = TRUE;
  8076. break;
  8077. }
  8078. ++i;
  8079. }
  8080. }
  8081. return (bRet);
  8082. }
  8083. ULONG
  8084. HandleRedirections(
  8085. IN PCHAR cmdLine,
  8086. IN ULONG nCharsStart,
  8087. OUT HANDLE *hInput,
  8088. OUT HANDLE *hOutput,
  8089. OUT HANDLE *hError
  8090. )
  8091. {
  8092. PCHAR Input = NULL, Output = NULL, Error = NULL;
  8093. PCHAR tmp = NULL;
  8094. ULONG retLength = nCharsStart;
  8095. ULONG jump = 0;
  8096. DWORD dwMode = OPEN_EXISTING;
  8097. SECURITY_ATTRIBUTES *sa = calloc( 1, sizeof(SECURITY_ATTRIBUTES));
  8098. if( sa == NULL ){
  8099. FPRINTF(stderr, "KERNRATE: Failed to allocate memory for security attributes in HandleRedirections()\n");
  8100. exit(1);
  8101. }
  8102. sa->bInheritHandle = TRUE;
  8103. tmp = strchr(cmdLine, '|');
  8104. if (tmp != NULL ){
  8105. FPRINTF(stderr, "\nKERNRATE: Piping '|' is not supported for '-o ProcessName {command line parameters}'\n");
  8106. FPRINTF(stderr, " Redirections of the input/output/error streams are supported.\n");
  8107. exit(1);
  8108. }
  8109. tmp = strchr(cmdLine, '<');
  8110. if ( tmp != NULL ){
  8111. PCHAR Startptr = tmp;
  8112. ULONG Length;
  8113. while(tmp[jump] == ' '){
  8114. ++jump;
  8115. }
  8116. Input = &tmp[jump];
  8117. Length = jump + lstrlen(Input);
  8118. *hInput = CreateFile(Input,
  8119. GENERIC_READ,
  8120. FILE_SHARE_READ,
  8121. sa,
  8122. OPEN_EXISTING,
  8123. FILE_ATTRIBUTE_NORMAL,
  8124. NULL
  8125. );
  8126. if (*hInput == INVALID_HANDLE_VALUE){
  8127. FPRINTF(stderr, "\nKERNRATE: Could not open user specified input file %s, attempting to continue\n", Input);
  8128. }
  8129. memcpy(Startptr, Startptr + Length, (retLength - (ULONG)(Startptr - cmdLine) - Length ) );
  8130. retLength -= Length;
  8131. cmdLine[retLength] = '\0';
  8132. }
  8133. jump = 0;
  8134. tmp = strstr(cmdLine, "2>>");
  8135. if( tmp != NULL ){
  8136. jump = 3;
  8137. dwMode = OPEN_ALWAYS;
  8138. } else {
  8139. tmp = strstr(cmdLine, "2>");
  8140. if( tmp != NULL ){
  8141. jump = 2;
  8142. dwMode = CREATE_ALWAYS;
  8143. }
  8144. }
  8145. if ( tmp != NULL ){
  8146. PCHAR Startptr = tmp;
  8147. ULONG Length;
  8148. while(tmp[jump] == ' '){
  8149. ++jump;
  8150. }
  8151. Error = &tmp[jump];
  8152. Length = jump + lstrlen(Error);
  8153. *hError = CreateFile(Error,
  8154. GENERIC_READ | GENERIC_WRITE,
  8155. FILE_SHARE_READ | FILE_SHARE_WRITE,
  8156. sa,
  8157. dwMode,
  8158. FILE_ATTRIBUTE_NORMAL,
  8159. NULL
  8160. );
  8161. if (*hError == INVALID_HANDLE_VALUE){
  8162. FPRINTF(stderr, "\nKERNRATE: Could not open or create user specified Error file %s, attempting to continue\n", Error);
  8163. }
  8164. if(dwMode == OPEN_ALWAYS){
  8165. SetFilePointer(*hError, 0, NULL, FILE_END);
  8166. }
  8167. memcpy(Startptr, Startptr + Length, (retLength - (ULONG)(Startptr - cmdLine) - Length ) );
  8168. retLength -= Length;
  8169. cmdLine[retLength] = '\0';
  8170. }
  8171. jump = 0;
  8172. tmp = strstr(cmdLine, "1>>");
  8173. if ( tmp != NULL ){
  8174. jump = 3;
  8175. dwMode = OPEN_ALWAYS;
  8176. } else {
  8177. tmp = strstr(cmdLine, "1>");
  8178. if ( tmp != NULL ){
  8179. jump = 2;
  8180. dwMode = CREATE_ALWAYS;
  8181. }
  8182. }
  8183. if ( tmp == NULL && hError == NULL ) {
  8184. tmp = strstr(cmdLine, ">>");
  8185. if (tmp != NULL){
  8186. jump = 2;
  8187. dwMode = OPEN_ALWAYS;
  8188. } else {
  8189. tmp = strchr(cmdLine, '>');
  8190. jump = 1;
  8191. dwMode = CREATE_ALWAYS;
  8192. }
  8193. }
  8194. if ( tmp != NULL ){
  8195. PCHAR Startptr = tmp;
  8196. ULONG Length;
  8197. while(tmp[jump] == ' '){
  8198. ++jump;
  8199. }
  8200. Output = &tmp[jump];
  8201. Length = jump + lstrlen(Output);
  8202. *hOutput = CreateFile(Output,
  8203. GENERIC_READ | GENERIC_WRITE,
  8204. FILE_SHARE_READ | FILE_SHARE_WRITE,
  8205. sa,
  8206. dwMode,
  8207. FILE_ATTRIBUTE_NORMAL,
  8208. NULL
  8209. );
  8210. if (*hOutput == INVALID_HANDLE_VALUE){
  8211. FPRINTF(stderr, "\nKERNRATE: Could not open or create user specified Output file %s, attempting to continue\n", Output);
  8212. }
  8213. if(dwMode == OPEN_ALWAYS){
  8214. SetFilePointer(*hOutput, 0, NULL, FILE_END);
  8215. }
  8216. memcpy(Startptr, Startptr + Length, (retLength - (ULONG)(Startptr - cmdLine) - Length ) );
  8217. retLength -= Length;
  8218. cmdLine[retLength] = '\0';
  8219. }
  8220. free(sa);
  8221. sa = NULL;
  8222. return (retLength);
  8223. }