1
0

profthrd.c 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. profthrd.c
  5. Abstract:
  6. This module implements generic support for thread profiling in the debuger.
  7. Author:
  8. Evan Green 14-Sep-2013
  9. Environment:
  10. Debug
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #define KERNEL_API
  16. #include "dbgrtl.h"
  17. #include <minoca/debug/spproto.h>
  18. #include <minoca/lib/im.h>
  19. #include <minoca/debug/dbgext.h>
  20. #include "symbols.h"
  21. #include "dbgapi.h"
  22. #include "dbgsym.h"
  23. #include "dbgrprof.h"
  24. #include "dbgprofp.h"
  25. #include "console.h"
  26. #include "dbgrcomm.h"
  27. #include <assert.h>
  28. #include <errno.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. //
  33. // ---------------------------------------------------------------- Definitions
  34. //
  35. #define THREAD_PROFILER_USAGE \
  36. "Usage: profiler thread <command> [options...]\n" \
  37. "This command works with context swap and thread lifetime information \n" \
  38. "sent from the target. Valid commands are:\n" \
  39. " clear - Delete all historical data stored in the debugger.\n" \
  40. " contextswaps [threadID...] - Write the thread context swap events \n" \
  41. " out to the debugger command console. A list of thread IDs \n" \
  42. " can be optionally specified to only print events related to \n" \
  43. " those threads. If not specified, data for all threads will \n" \
  44. " be printed.\n" \
  45. " list - Write a summary of all processes and threads contained in \n" \
  46. " the data.\n" \
  47. " blockingqueues [threadID...] - Dump a list of blocking wait queues \n" \
  48. " threads are waiting on, sorted in descending order by the \n" \
  49. " number of times that queue has been blocked on. The list \n" \
  50. " can be optionally restricted to queues waited on by the \n" \
  51. " given list of thread IDs.\n" \
  52. " help - Display this help.\n\n"
  53. #define INITIAL_POINTER_ARRAY_CAPACITY 16
  54. //
  55. // ------------------------------------------------------ Data Type Definitions
  56. //
  57. /*++
  58. Structure Description:
  59. This structure defines a context swap event.
  60. Members:
  61. Processor - Stores the processor number this context swap event is
  62. associated with.
  63. Event - Stores a pointer to the actual event.
  64. --*/
  65. typedef struct _CONTEXT_SWAP_EVENT {
  66. ULONG Processor;
  67. PROFILER_CONTEXT_SWAP Event;
  68. } CONTEXT_SWAP_EVENT, *PCONTEXT_SWAP_EVENT;
  69. /*++
  70. Structure Description:
  71. This structure defines information about a blocking queue.
  72. Members:
  73. Queue - Stores the pointer to the blocking queue.
  74. TotalWaitDuration - Stores the total amount of time all threads have waited
  75. on the queue, in time counter ticks.
  76. TotalWaitCount - Stores the number of waits that have occurred on this
  77. queue.
  78. ThreadList - Stores the array of threads that have waited on the queue.
  79. --*/
  80. typedef struct _PROFILER_BLOCKING_QUEUE {
  81. ULONGLONG Queue;
  82. ULONGLONG TotalWaitDuration;
  83. ULONGLONG TotalWaitCount;
  84. PPOINTER_ARRAY ThreadList;
  85. } PROFILER_BLOCKING_QUEUE, *PPROFILER_BLOCKING_QUEUE;
  86. /*++
  87. Structure Description:
  88. This structure defines information about a thread blocking on an object.
  89. Members:
  90. ProcessId - Stores the ID of the process that owns the thread.
  91. ThreadId - Stores the ID of the thread that waited.
  92. TotalWaitDuration - Stores the total amount of time all the thread has
  93. waited on the object, in time counter ticks.
  94. TotalWaitCount - Stores the number of waits that have occurred on this
  95. object.
  96. --*/
  97. typedef struct _PROFILER_BLOCKING_THREAD {
  98. ULONG ProcessId;
  99. ULONG ThreadId;
  100. ULONGLONG TotalWaitDuration;
  101. ULONGLONG TotalWaitCount;
  102. } PROFILER_BLOCKING_THREAD, *PPROFILER_BLOCKING_THREAD;
  103. typedef
  104. VOID
  105. (*PPOINTER_ARRAY_ITERATE_ROUTINE) (
  106. PVOID Element
  107. );
  108. /*++
  109. Routine Description:
  110. This routine is called once for each element in a pointer array.
  111. Arguments:
  112. Element - Supplies a pointer to the element.
  113. Return Value:
  114. None.
  115. --*/
  116. //
  117. // ----------------------------------------------- Internal Function Prototypes
  118. //
  119. VOID
  120. DbgrpDisplayContextSwaps (
  121. PDEBUGGER_CONTEXT Context,
  122. PULONG ThreadList,
  123. ULONG ThreadListSize
  124. );
  125. VOID
  126. DbgrpListProcessesAndThreads (
  127. PDEBUGGER_CONTEXT Context
  128. );
  129. VOID
  130. DbgrpDisplayBlockingQueues (
  131. PDEBUGGER_CONTEXT Context,
  132. PULONG ThreadList,
  133. ULONG ThreadListSize
  134. );
  135. VOID
  136. DbgrpFullyProcessThreadProfilingData (
  137. PDEBUGGER_CONTEXT Context
  138. );
  139. VOID
  140. DbgrpClearThreadProfilingData (
  141. PDEBUGGER_CONTEXT Context
  142. );
  143. PULONG
  144. DbgrpCreateThreadIdArray (
  145. PSTR *Arguments,
  146. ULONG ArgumentCount
  147. );
  148. PSTR
  149. DbgpGetProcessName (
  150. PDEBUGGER_CONTEXT Context,
  151. ULONG ProcessId,
  152. PSTR NumericBuffer,
  153. ULONG NumericBufferSize
  154. );
  155. PSTR
  156. DbgrpGetThreadName (
  157. PDEBUGGER_CONTEXT Context,
  158. ULONG ThreadId,
  159. PSTR NumericBuffer,
  160. ULONG NumericBufferSize
  161. );
  162. int
  163. DbgrpCompareContextSwapsByTimeAscending (
  164. const void *LeftPointer,
  165. const void *RightPointer
  166. );
  167. BOOL
  168. DbgrpReadFromProfilingBuffers (
  169. PLIST_ENTRY ListHead,
  170. PVOID Buffer,
  171. ULONG Size,
  172. BOOL Consume
  173. );
  174. PPOINTER_ARRAY
  175. DbgrpCreatePointerArray (
  176. ULONGLONG InitialCapacity
  177. );
  178. VOID
  179. DbgrpDestroyPointerArray (
  180. PPOINTER_ARRAY Array,
  181. PPOINTER_ARRAY_ITERATE_ROUTINE DestroyRoutine
  182. );
  183. BOOL
  184. DbgrpPointerArrayAddElement (
  185. PPOINTER_ARRAY Array,
  186. PVOID Element
  187. );
  188. VOID
  189. DbgrpCalculateDuration (
  190. ULONGLONG Duration,
  191. ULONGLONG Frequency,
  192. PULONGLONG TimeDuration,
  193. PSTR *Units,
  194. PBOOL TimesTen
  195. );
  196. VOID
  197. DbgrpDestroyBlockingQueue (
  198. PVOID Queue
  199. );
  200. //
  201. // -------------------------------------------------------------------- Globals
  202. //
  203. //
  204. // ------------------------------------------------------------------ Functions
  205. //
  206. INT
  207. DbgrpInitializeThreadProfiling (
  208. PDEBUGGER_CONTEXT Context
  209. )
  210. /*++
  211. Routine Description:
  212. This routine initializes support for thread profiling.
  213. Arguments:
  214. Context - Supplies a pointer to the debugger context.
  215. Return Value:
  216. 0 on success.
  217. Returns an error code on failure.
  218. --*/
  219. {
  220. Context->ThreadProfiling.StatisticsListLock = CreateDebuggerLock();
  221. if (Context->ThreadProfiling.StatisticsListLock == NULL) {
  222. return ENOMEM;
  223. }
  224. Context->ThreadProfiling.StatisticsLock = CreateDebuggerLock();
  225. if (Context->ThreadProfiling.StatisticsLock == NULL) {
  226. return ENOMEM;
  227. }
  228. Context->ThreadProfiling.ContextSwaps = DbgrpCreatePointerArray(0);
  229. if (Context->ThreadProfiling.ContextSwaps == NULL) {
  230. return ENOMEM;
  231. }
  232. Context->ThreadProfiling.Processes = DbgrpCreatePointerArray(0);
  233. if (Context->ThreadProfiling.Processes == NULL) {
  234. return ENOMEM;
  235. }
  236. Context->ThreadProfiling.Threads = DbgrpCreatePointerArray(0);
  237. if (Context->ThreadProfiling.Threads == NULL) {
  238. return ENOMEM;
  239. }
  240. INITIALIZE_LIST_HEAD(&(Context->ThreadProfiling.StatisticsListHead));
  241. Context->ThreadProfiling.ProcessNameWidth = 5;
  242. Context->ThreadProfiling.ThreadNameWidth = 5;
  243. return 0;
  244. }
  245. VOID
  246. DbgrpDestroyThreadProfiling (
  247. PDEBUGGER_CONTEXT Context
  248. )
  249. /*++
  250. Routine Description:
  251. This routine destroys any structures used for thread profiling.
  252. Arguments:
  253. Context - Supplies a pointer to the application context.
  254. Return Value:
  255. None.
  256. --*/
  257. {
  258. if (Context->ThreadProfiling.StatisticsListLock != NULL) {
  259. DbgrpClearThreadProfilingData(Context);
  260. if (Context->ThreadProfiling.Processes != NULL) {
  261. DbgrpDestroyPointerArray(Context->ThreadProfiling.Processes, free);
  262. Context->ThreadProfiling.Processes = NULL;
  263. }
  264. if (Context->ThreadProfiling.Threads != NULL) {
  265. DbgrpDestroyPointerArray(Context->ThreadProfiling.Threads, free);
  266. Context->ThreadProfiling.Threads = NULL;
  267. }
  268. DestroyDebuggerLock(Context->ThreadProfiling.StatisticsLock);
  269. DestroyDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  270. DbgrpDestroyPointerArray(Context->ThreadProfiling.ContextSwaps, free);
  271. Context->ThreadProfiling.ContextSwaps = NULL;
  272. }
  273. return;
  274. }
  275. VOID
  276. DbgrpProcessThreadProfilingData (
  277. PDEBUGGER_CONTEXT Context,
  278. PPROFILER_DATA_ENTRY ProfilerData
  279. )
  280. /*++
  281. Routine Description:
  282. This routine processes a profiler notification that the debuggee sends to
  283. the debugger. The routine should collect the profiler data and return as
  284. quickly as possible.
  285. Arguments:
  286. Context - Supplies a pointer to the application context.
  287. ProfilerData - Supplies a pointer to the newly allocated data. This routine
  288. will take ownership of that allocation.
  289. Return Value:
  290. None.
  291. --*/
  292. {
  293. AcquireDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  294. INSERT_BEFORE(&(ProfilerData->ListEntry),
  295. &(Context->ThreadProfiling.StatisticsListHead));
  296. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  297. return;
  298. }
  299. INT
  300. DbgrpDispatchThreadProfilerCommand (
  301. PDEBUGGER_CONTEXT Context,
  302. PSTR *Arguments,
  303. ULONG ArgumentCount
  304. )
  305. /*++
  306. Routine Description:
  307. This routine handles a thread profiler command.
  308. Arguments:
  309. Context - Supplies a pointer to the application context.
  310. Arguments - Supplies an array of strings containing the arguments.
  311. ArgumentCount - Supplies the number of arguments in the Arguments array.
  312. Return Value:
  313. 0 on success.
  314. Returns an error code on failure.
  315. --*/
  316. {
  317. PULONG ThreadList;
  318. ULONG ThreadListSize;
  319. assert(strcasecmp(Arguments[0], "thread") == 0);
  320. ThreadList = NULL;
  321. if (ArgumentCount < 2) {
  322. DbgOut(THREAD_PROFILER_USAGE);
  323. return EINVAL;
  324. }
  325. if (strcasecmp(Arguments[1], "clear") == 0) {
  326. DbgrpClearThreadProfilingData(Context);
  327. } else if (strcasecmp(Arguments[1], "contextswaps") == 0) {
  328. DbgrpFullyProcessThreadProfilingData(Context);
  329. ThreadListSize = 0;
  330. if (ArgumentCount > 2) {
  331. ThreadListSize = ArgumentCount - 2;
  332. ThreadList = DbgrpCreateThreadIdArray(Arguments + 2,
  333. ThreadListSize);
  334. }
  335. DbgrpDisplayContextSwaps(Context, ThreadList, ThreadListSize);
  336. } else if (strcasecmp(Arguments[1], "list") == 0) {
  337. DbgrpFullyProcessThreadProfilingData(Context);
  338. DbgrpListProcessesAndThreads(Context);
  339. } else if (strcasecmp(Arguments[1], "blockingqueues") == 0) {
  340. DbgrpFullyProcessThreadProfilingData(Context);
  341. ThreadListSize = 0;
  342. if (ArgumentCount > 2) {
  343. ThreadListSize = ArgumentCount - 2;
  344. ThreadList = DbgrpCreateThreadIdArray(Arguments + 2,
  345. ThreadListSize);
  346. }
  347. DbgrpDisplayBlockingQueues(Context, ThreadList, ThreadListSize);
  348. } else if (strcasecmp(Arguments[1], "help") == 0) {
  349. DbgOut(THREAD_PROFILER_USAGE);
  350. }
  351. if (ThreadList != NULL) {
  352. free(ThreadList);
  353. }
  354. return 0;
  355. }
  356. //
  357. // --------------------------------------------------------- Internal Functions
  358. //
  359. VOID
  360. DbgrpDisplayContextSwaps (
  361. PDEBUGGER_CONTEXT Context,
  362. PULONG ThreadList,
  363. ULONG ThreadListSize
  364. )
  365. /*++
  366. Routine Description:
  367. This routine prints the current context swap data.
  368. Arguments:
  369. Context - Supplies a pointer to the application context.
  370. ThreadList - Supplies an optional pointer to an array of threads to limit
  371. the output to.
  372. ThreadListSize - Supplies the number of elements in the thread list.
  373. Return Value:
  374. None.
  375. --*/
  376. {
  377. ULONG AllocationSize;
  378. PCONTEXT_SWAP_EVENT *Array;
  379. ULONGLONG Count;
  380. ULONGLONG Duration;
  381. PSTR DurationUnits;
  382. PCONTEXT_SWAP_EVENT Event;
  383. ULONGLONG Frequency;
  384. ULONGLONG Index;
  385. PULONGLONG PreviousCounts;
  386. PSTR ProcessName;
  387. CHAR ProcessNumberString[10];
  388. PSTR Reason;
  389. ULONG SearchIndex;
  390. PSTR ThreadName;
  391. CHAR ThreadNumberString[10];
  392. PDEBUGGER_THREAD_PROFILING_DATA ThreadProfiling;
  393. BOOL TimesTen;
  394. ThreadProfiling = &(Context->ThreadProfiling);
  395. if ((ThreadProfiling->ContextSwaps == NULL) ||
  396. (ThreadProfiling->ContextSwaps->Size == 0)) {
  397. DbgOut("No context swap data.\n");
  398. return;
  399. }
  400. //
  401. // Allocate space to remember the previous time counter values for each
  402. // processor.
  403. //
  404. AllocationSize = ThreadProfiling->ProcessorCount * sizeof(ULONGLONG);
  405. PreviousCounts = malloc(AllocationSize);
  406. if (PreviousCounts == NULL) {
  407. return;
  408. }
  409. memset(PreviousCounts, 0, AllocationSize);
  410. //
  411. // Sort the context swap data by counter.
  412. //
  413. Array = (PCONTEXT_SWAP_EVENT *)(ThreadProfiling->ContextSwaps->Elements);
  414. Count = Context->ThreadProfiling.ContextSwaps->Size;
  415. qsort(Array,
  416. Count,
  417. sizeof(PVOID),
  418. DbgrpCompareContextSwapsByTimeAscending);
  419. Frequency = ThreadProfiling->ReferenceTime.TimeCounterFrequency;
  420. for (Index = 1; Index < Count; Index += 1) {
  421. Event = Array[Index];
  422. //
  423. // Figure out the duration of this event.
  424. //
  425. assert(Event->Processor < ThreadProfiling->ProcessorCount);
  426. if (PreviousCounts[Event->Processor] == 0) {
  427. Duration = 0;
  428. } else {
  429. Duration = Event->Event.TimeCount -
  430. PreviousCounts[Event->Processor];
  431. }
  432. PreviousCounts[Event->Processor] = Event->Event.TimeCount;
  433. //
  434. // If there's a filter list, try to find this thread in it.
  435. //
  436. if (ThreadListSize != 0) {
  437. for (SearchIndex = 0;
  438. SearchIndex < ThreadListSize;
  439. SearchIndex += 1) {
  440. if (ThreadList[SearchIndex] == Event->Event.ThreadId) {
  441. break;
  442. }
  443. }
  444. if (SearchIndex == ThreadListSize) {
  445. continue;
  446. }
  447. }
  448. DbgrpCalculateDuration(Duration,
  449. Frequency,
  450. &Duration,
  451. &DurationUnits,
  452. &TimesTen);
  453. switch (Event->Event.EventType) {
  454. case ProfilerThreadEventPreemption:
  455. Reason = "preempted";
  456. break;
  457. case ProfilerThreadEventBlocking:
  458. Reason = "blocked";
  459. break;
  460. case ProfilerThreadEventYielding:
  461. Reason = "yielded";
  462. break;
  463. case ProfilerThreadEventSuspending:
  464. Reason = "suspended";
  465. break;
  466. case ProfilerThreadEventExiting:
  467. Reason = "exited";
  468. break;
  469. default:
  470. Reason = "unknown";
  471. break;
  472. }
  473. ProcessName = DbgpGetProcessName(Context,
  474. Event->Event.ProcessId,
  475. ProcessNumberString,
  476. sizeof(ProcessNumberString));
  477. ThreadName = DbgrpGetThreadName(Context,
  478. Event->Event.ThreadId,
  479. ThreadNumberString,
  480. sizeof(ThreadNumberString));
  481. if (TimesTen != FALSE) {
  482. DbgOut("%3d %*s %*s %3I64d.%d%-2s %9s",
  483. Event->Processor,
  484. Context->ThreadProfiling.ProcessNameWidth,
  485. ProcessName,
  486. Context->ThreadProfiling.ThreadNameWidth,
  487. ThreadName,
  488. Duration / 10UL,
  489. (ULONG)(Duration % 10),
  490. DurationUnits,
  491. Reason);
  492. } else {
  493. DbgOut("%3d %*s %*s %5I64d%-2s %9s",
  494. Event->Processor,
  495. Context->ThreadProfiling.ProcessNameWidth,
  496. ProcessName,
  497. Context->ThreadProfiling.ThreadNameWidth,
  498. ThreadName,
  499. Duration,
  500. DurationUnits,
  501. Reason);
  502. }
  503. if (Event->Event.BlockingQueue != 0) {
  504. DbgOut(" %I64x\n", Event->Event.BlockingQueue);
  505. } else {
  506. DbgOut("\n");
  507. }
  508. }
  509. free(PreviousCounts);
  510. return;
  511. }
  512. VOID
  513. DbgrpListProcessesAndThreads (
  514. PDEBUGGER_CONTEXT Context
  515. )
  516. /*++
  517. Routine Description:
  518. This routine lists all the processes and threads in the thread profiling
  519. data.
  520. Arguments:
  521. Context - Supplies a pointer to the application context.
  522. Return Value:
  523. None.
  524. --*/
  525. {
  526. PPROFILER_THREAD_NEW_PROCESS Process;
  527. PPOINTER_ARRAY Processes;
  528. ULONGLONG ProcessIndex;
  529. PPROFILER_THREAD_NEW_THREAD Thread;
  530. ULONGLONG ThreadIndex;
  531. PPOINTER_ARRAY Threads;
  532. Processes = Context->ThreadProfiling.Processes;
  533. Threads = Context->ThreadProfiling.Threads;
  534. if (((Processes == NULL) || (Processes->Size == 0)) &&
  535. ((Threads == NULL) || (Threads->Size == 0))) {
  536. DbgOut("No data received.\n");
  537. return;
  538. }
  539. DbgOut("Process Legend: StartTime ProcessId Name\n");
  540. DbgOut("Thread Legend: StartTime ProcessId ThreadId Name\n");
  541. if (Processes != NULL) {
  542. for (ProcessIndex = 0;
  543. ProcessIndex < Processes->Size;
  544. ProcessIndex += 1) {
  545. Process = Processes->Elements[ProcessIndex];
  546. DbgOut("Process %16I64x %d %s\n",
  547. Process->TimeCounter,
  548. Process->ProcessId,
  549. Process->Name);
  550. if (Threads != NULL) {
  551. for (ThreadIndex = 0;
  552. ThreadIndex < Threads->Size;
  553. ThreadIndex += 1) {
  554. Thread = Threads->Elements[ThreadIndex];
  555. if (Thread->ProcessId != Process->ProcessId) {
  556. continue;
  557. }
  558. DbgOut(" Thread %16I64x %d %d %s\n",
  559. Thread->TimeCounter,
  560. Thread->ProcessId,
  561. Thread->ThreadId,
  562. Thread->Name);
  563. }
  564. }
  565. }
  566. }
  567. return;
  568. }
  569. VOID
  570. DbgrpDisplayBlockingQueues (
  571. PDEBUGGER_CONTEXT Context,
  572. PULONG ThreadList,
  573. ULONG ThreadListSize
  574. )
  575. /*++
  576. Routine Description:
  577. This routine prints a summary of the wait queues generally blocked on.
  578. Arguments:
  579. Context - Supplies a pointer to the application context.
  580. ThreadList - Supplies an optional pointer to an array of threads to limit
  581. the output to.
  582. ThreadListSize - Supplies the number of elements in the thread list.
  583. Return Value:
  584. None.
  585. --*/
  586. {
  587. ULONG AllocationSize;
  588. PCONTEXT_SWAP_EVENT *Array;
  589. ULONGLONG Count;
  590. ULONGLONG Duration;
  591. PSTR DurationUnits;
  592. PCONTEXT_SWAP_EVENT Event;
  593. ULONGLONG Frequency;
  594. ULONGLONG Index;
  595. PCONTEXT_SWAP_EVENT NextEvent;
  596. PULONGLONG PreviousCounts;
  597. PCONTEXT_SWAP_EVENT PreviousNextEvent;
  598. PSTR ProcessName;
  599. CHAR ProcessNumberString[10];
  600. PPROFILER_BLOCKING_QUEUE Queue;
  601. PPOINTER_ARRAY Queues;
  602. BOOL Result;
  603. ULONG SearchIndex;
  604. PPROFILER_BLOCKING_THREAD Thread;
  605. ULONGLONG ThreadIndex;
  606. PSTR ThreadName;
  607. CHAR ThreadNumberString[10];
  608. PDEBUGGER_THREAD_PROFILING_DATA ThreadProfiling;
  609. BOOL TimesTen;
  610. ThreadProfiling = &(Context->ThreadProfiling);
  611. if ((ThreadProfiling->ContextSwaps == NULL) ||
  612. (ThreadProfiling->ContextSwaps->Size == 0)) {
  613. DbgOut("No context swap data.\n");
  614. return;
  615. }
  616. Queues = NULL;
  617. //
  618. // Allocate space to remember the previous time counter values for each
  619. // processor.
  620. //
  621. AllocationSize = ThreadProfiling->ProcessorCount * sizeof(ULONGLONG);
  622. PreviousCounts = malloc(AllocationSize);
  623. if (PreviousCounts == NULL) {
  624. goto DisplayBlockingQueuesEnd;
  625. }
  626. Queues = DbgrpCreatePointerArray(0);
  627. if (Queues == NULL) {
  628. goto DisplayBlockingQueuesEnd;
  629. }
  630. memset(PreviousCounts, 0, AllocationSize);
  631. //
  632. // Sort the context swap data by counter.
  633. //
  634. Array = (PCONTEXT_SWAP_EVENT *)(ThreadProfiling->ContextSwaps->Elements);
  635. Count = ThreadProfiling->ContextSwaps->Size;
  636. qsort(Array,
  637. Count,
  638. sizeof(PVOID),
  639. DbgrpCompareContextSwapsByTimeAscending);
  640. Frequency = ThreadProfiling->ReferenceTime.TimeCounterFrequency;
  641. for (Index = 1; Index < Count; Index += 1) {
  642. Event = Array[Index];
  643. PreviousCounts[Event->Processor] = Event->Event.TimeCount;
  644. //
  645. // If there's a filter list, try to find this thread in it.
  646. //
  647. if (ThreadListSize != 0) {
  648. for (SearchIndex = 0;
  649. SearchIndex < ThreadListSize;
  650. SearchIndex += 1) {
  651. if (ThreadList[SearchIndex] == Event->Event.ThreadId) {
  652. break;
  653. }
  654. }
  655. if (SearchIndex == ThreadListSize) {
  656. continue;
  657. }
  658. }
  659. //
  660. // Skip it if it's not a blocking event.
  661. //
  662. if (Event->Event.BlockingQueue == 0) {
  663. continue;
  664. }
  665. //
  666. // Find the blocking queue structure, or create one if it's new.
  667. //
  668. for (SearchIndex = 0; SearchIndex < Queues->Size; SearchIndex += 1) {
  669. Queue = Queues->Elements[SearchIndex];
  670. if (Queue->Queue == Event->Event.BlockingQueue) {
  671. break;
  672. }
  673. }
  674. if (SearchIndex == Queues->Size) {
  675. Queue = malloc(sizeof(PROFILER_BLOCKING_QUEUE));
  676. if (Queue == NULL) {
  677. goto DisplayBlockingQueuesEnd;
  678. }
  679. memset(Queue, 0, sizeof(PROFILER_BLOCKING_QUEUE));
  680. Queue->Queue = Event->Event.BlockingQueue;
  681. Result = DbgrpPointerArrayAddElement(Queues, Queue);
  682. if (Result == FALSE) {
  683. free(Queue);
  684. goto DisplayBlockingQueuesEnd;
  685. }
  686. Queue->ThreadList = DbgrpCreatePointerArray(0);
  687. if (Queue->ThreadList == NULL) {
  688. goto DisplayBlockingQueuesEnd;
  689. }
  690. }
  691. //
  692. // Find the blocking thread, or create one.
  693. //
  694. for (SearchIndex = 0;
  695. SearchIndex < Queue->ThreadList->Size;
  696. SearchIndex += 1) {
  697. Thread = Queue->ThreadList->Elements[SearchIndex];
  698. if (Thread->ThreadId == Event->Event.ThreadId) {
  699. break;
  700. }
  701. }
  702. if (SearchIndex == Queue->ThreadList->Size) {
  703. Thread = malloc(sizeof(PROFILER_BLOCKING_THREAD));
  704. if (Thread == NULL) {
  705. goto DisplayBlockingQueuesEnd;
  706. }
  707. memset(Thread, 0, sizeof(PROFILER_BLOCKING_THREAD));
  708. Thread->ThreadId = Event->Event.ThreadId;
  709. Thread->ProcessId = Event->Event.ProcessId;
  710. Result = DbgrpPointerArrayAddElement(Queue->ThreadList, Thread);
  711. if (Result == FALSE) {
  712. goto DisplayBlockingQueuesEnd;
  713. }
  714. }
  715. //
  716. // Attempt to find the next time the thread was run to figure out how
  717. // long it blocked for.
  718. //
  719. NextEvent = NULL;
  720. for (SearchIndex = Index + 1; SearchIndex < Count; SearchIndex += 1) {
  721. NextEvent = Array[SearchIndex];
  722. if (NextEvent->Event.ThreadId == Event->Event.ThreadId) {
  723. break;
  724. }
  725. }
  726. //
  727. // The next event timestamps when that thread was swapped out. Find the
  728. // previous event on that same processor to figure out when it was
  729. // swapped in. That then represents the total wait time.
  730. //
  731. if (SearchIndex != Count) {
  732. SearchIndex -= 1;
  733. while (SearchIndex >= Index + 1) {
  734. PreviousNextEvent = Array[SearchIndex];
  735. if (PreviousNextEvent->Processor == NextEvent->Processor) {
  736. break;
  737. }
  738. SearchIndex -= 1;
  739. }
  740. //
  741. // If all that worked, add the wait duration information to the
  742. // thread and queue.
  743. //
  744. if (SearchIndex >= Index + 1) {
  745. if (PreviousNextEvent->Event.TimeCount <
  746. Event->Event.TimeCount) {
  747. DbgOut("TimeCounter appeared to move backwards from "
  748. "%I64x to %I64x.\n",
  749. Event->Event.TimeCount,
  750. PreviousNextEvent->Event.TimeCount);
  751. } else {
  752. Duration = PreviousNextEvent->Event.TimeCount -
  753. Event->Event.TimeCount;
  754. Thread->TotalWaitCount += 1;
  755. Thread->TotalWaitDuration += Duration;
  756. Queue->TotalWaitDuration += Duration;
  757. Queue->TotalWaitCount += 1;
  758. }
  759. }
  760. }
  761. }
  762. //
  763. // Loop through all the constructed queues printing them out.
  764. //
  765. DbgOut("Queue Legend: Queue BlockCount AverageBlockingDuration\n");
  766. DbgOut("Thread Legend: Process Thread BlockCount "
  767. "AverageBlockingDuration\n");
  768. for (Index = 0; Index < Queues->Size; Index += 1) {
  769. Queue = Queues->Elements[Index];
  770. Duration = 0;
  771. if (Queue->TotalWaitCount != 0) {
  772. Duration = Queue->TotalWaitDuration / Queue->TotalWaitCount;
  773. }
  774. DbgrpCalculateDuration(Duration,
  775. Frequency,
  776. &Duration,
  777. &DurationUnits,
  778. &TimesTen);
  779. if (TimesTen != FALSE) {
  780. DbgOut("%08I64x %6I64d %I64d.%d%-2s\n",
  781. Queue->Queue,
  782. Queue->TotalWaitCount,
  783. Duration / 10UL,
  784. (ULONG)(Duration % 10),
  785. DurationUnits);
  786. } else {
  787. DbgOut("%08I64x %6I64d %I64d%-2s\n",
  788. Queue->Queue,
  789. Queue->TotalWaitCount,
  790. Duration,
  791. DurationUnits);
  792. }
  793. //
  794. // Print out all threads that got stuck on this object.
  795. //
  796. for (ThreadIndex = 0;
  797. ThreadIndex < Queue->ThreadList->Size;
  798. ThreadIndex += 1) {
  799. Thread = Queue->ThreadList->Elements[ThreadIndex];
  800. ProcessName = DbgpGetProcessName(Context,
  801. Thread->ProcessId,
  802. ProcessNumberString,
  803. sizeof(ProcessNumberString));
  804. ThreadName = DbgrpGetThreadName(Context,
  805. Thread->ThreadId,
  806. ThreadNumberString,
  807. sizeof(ThreadNumberString));
  808. Duration = 0;
  809. if (Thread->TotalWaitCount != 0) {
  810. Duration = Thread->TotalWaitDuration / Thread->TotalWaitCount;
  811. DbgrpCalculateDuration(Duration,
  812. Frequency,
  813. &Duration,
  814. &DurationUnits,
  815. &TimesTen);
  816. if (TimesTen != FALSE) {
  817. DbgOut(" %*s %*s %6I64d %I64d.%d%-2s\n",
  818. Context->ThreadProfiling.ProcessNameWidth,
  819. ProcessName,
  820. Context->ThreadProfiling.ThreadNameWidth,
  821. ThreadName,
  822. Thread->TotalWaitCount,
  823. Duration / 10UL,
  824. (ULONG)(Duration % 10),
  825. DurationUnits);
  826. } else {
  827. DbgOut(" %*s %*s %6I64d %I64d%-2s\n",
  828. Context->ThreadProfiling.ProcessNameWidth,
  829. ProcessName,
  830. Context->ThreadProfiling.ThreadNameWidth,
  831. ThreadName,
  832. Thread->TotalWaitCount,
  833. Duration,
  834. DurationUnits);
  835. }
  836. }
  837. }
  838. DbgOut("\n");
  839. }
  840. DisplayBlockingQueuesEnd:
  841. if (PreviousCounts != NULL) {
  842. free(PreviousCounts);
  843. }
  844. if (Queues != NULL) {
  845. DbgrpDestroyPointerArray(Queues, DbgrpDestroyBlockingQueue);
  846. }
  847. return;
  848. }
  849. VOID
  850. DbgrpFullyProcessThreadProfilingData (
  851. PDEBUGGER_CONTEXT Context
  852. )
  853. /*++
  854. Routine Description:
  855. This routine processes unhandled thread profiling data, sorting its
  856. events into the proper pointer arrays.
  857. Arguments:
  858. Context - Supplies a pointer to the application context.
  859. Return Value:
  860. None.
  861. --*/
  862. {
  863. PCONTEXT_SWAP_EVENT ContextSwap;
  864. PLIST_ENTRY CurrentEntry;
  865. UCHAR EventType;
  866. ULONG Length;
  867. LIST_ENTRY LocalList;
  868. PPROFILER_THREAD_NEW_PROCESS NewProcess;
  869. PPROFILER_THREAD_NEW_THREAD NewThread;
  870. PROFILER_THREAD_NEW_PROCESS Process;
  871. PSTR ProcessName;
  872. PPROFILER_DATA_ENTRY ProfilerData;
  873. ULONG RemainingSize;
  874. BOOL Result;
  875. PROFILER_THREAD_NEW_THREAD Thread;
  876. PSTR ThreadName;
  877. //
  878. // Pull everything off of the unprocessed list as quickly as possible so
  879. // as not to block inoming profiling data notifications.
  880. //
  881. AcquireDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  882. if (LIST_EMPTY(&(Context->ThreadProfiling.StatisticsListHead)) != FALSE) {
  883. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  884. return;
  885. }
  886. LocalList.Next = Context->ThreadProfiling.StatisticsListHead.Next;
  887. LocalList.Previous = Context->ThreadProfiling.StatisticsListHead.Previous;
  888. LocalList.Next->Previous = &LocalList;
  889. LocalList.Previous->Next = &LocalList;
  890. INITIALIZE_LIST_HEAD(&(Context->ThreadProfiling.StatisticsListHead));
  891. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  892. //
  893. // Loop through the entries to find and take note of the maximum processor
  894. // number.
  895. //
  896. AcquireDebuggerLock(Context->ThreadProfiling.StatisticsLock);
  897. CurrentEntry = LocalList.Next;
  898. while (CurrentEntry != &LocalList) {
  899. ProfilerData = LIST_VALUE(CurrentEntry,
  900. PROFILER_DATA_ENTRY,
  901. ListEntry);
  902. if (ProfilerData->Processor + 1 >
  903. Context->ThreadProfiling.ProcessorCount) {
  904. Context->ThreadProfiling.ProcessorCount =
  905. ProfilerData->Processor + 1;
  906. }
  907. CurrentEntry = CurrentEntry->Next;
  908. }
  909. //
  910. // Loop through all the data in the entry, switching based on the
  911. // first byte which signifies the event type.
  912. //
  913. while (TRUE) {
  914. Result = DbgrpReadFromProfilingBuffers(&LocalList,
  915. &EventType,
  916. 1,
  917. FALSE);
  918. if (Result == FALSE) {
  919. break;
  920. }
  921. assert(LIST_EMPTY(&LocalList) == FALSE);
  922. ProfilerData = LIST_VALUE(LocalList.Next,
  923. PROFILER_DATA_ENTRY,
  924. ListEntry);
  925. if ((EventType >= ProfilerThreadEventAlternateMin) &&
  926. (EventType < ProfilerThreadEventMax)) {
  927. switch (EventType) {
  928. case ProfilerThreadEventNewProcess:
  929. Result = DbgrpReadFromProfilingBuffers(
  930. &LocalList,
  931. &Process,
  932. sizeof(PROFILER_THREAD_NEW_PROCESS),
  933. TRUE);
  934. if (Result == FALSE) {
  935. break;
  936. }
  937. if (Process.StructureSize > 0x1000) {
  938. DbgOut("Got a process with giant size %x. Skipping.\n",
  939. Process.StructureSize);
  940. break;
  941. }
  942. NewProcess = malloc(Process.StructureSize);
  943. if (NewProcess == NULL) {
  944. Result = FALSE;
  945. break;
  946. }
  947. RtlCopyMemory(NewProcess,
  948. &Process,
  949. sizeof(PROFILER_THREAD_NEW_PROCESS));
  950. RemainingSize = Process.StructureSize -
  951. sizeof(PROFILER_THREAD_NEW_PROCESS);
  952. Result = DbgrpReadFromProfilingBuffers(&LocalList,
  953. NewProcess + 1,
  954. RemainingSize,
  955. TRUE);
  956. if (Result == FALSE) {
  957. free(NewProcess);
  958. break;
  959. }
  960. ProcessName = (PSTR)(NewProcess->Name);
  961. ProcessName[RemainingSize] = '\0';
  962. Result = DbgrpPointerArrayAddElement(
  963. Context->ThreadProfiling.Processes,
  964. NewProcess);
  965. if (Result == FALSE) {
  966. free(NewProcess);
  967. break;
  968. }
  969. Length = RtlStringLength(NewProcess->Name);
  970. if (Length > Context->ThreadProfiling.ProcessNameWidth) {
  971. Context->ThreadProfiling.ProcessNameWidth = Length;
  972. }
  973. break;
  974. case ProfilerThreadEventNewThread:
  975. Result = DbgrpReadFromProfilingBuffers(
  976. &LocalList,
  977. &Thread,
  978. sizeof(PROFILER_THREAD_NEW_THREAD),
  979. TRUE);
  980. if (Result == FALSE) {
  981. break;
  982. }
  983. if (Thread.StructureSize > 0x1000) {
  984. DbgOut("Got a thread with giant size %x. Skipping.\n",
  985. Thread.StructureSize);
  986. break;
  987. }
  988. NewThread = malloc(Thread.StructureSize);
  989. if (NewThread == NULL) {
  990. Result = FALSE;
  991. break;
  992. }
  993. RtlCopyMemory(NewThread,
  994. &Thread,
  995. sizeof(PROFILER_THREAD_NEW_THREAD));
  996. RemainingSize = Thread.StructureSize -
  997. sizeof(PROFILER_THREAD_NEW_THREAD);
  998. Result = DbgrpReadFromProfilingBuffers(&LocalList,
  999. NewThread + 1,
  1000. RemainingSize,
  1001. TRUE);
  1002. if (Result == FALSE) {
  1003. free(NewThread);
  1004. break;
  1005. }
  1006. ThreadName = (PSTR)(NewThread->Name);
  1007. ThreadName[RemainingSize] = '\0';
  1008. Result = DbgrpPointerArrayAddElement(
  1009. Context->ThreadProfiling.Threads,
  1010. NewThread);
  1011. if (Result == FALSE) {
  1012. free(NewThread);
  1013. break;
  1014. }
  1015. Length = RtlStringLength(NewThread->Name);
  1016. if (Length > Context->ThreadProfiling.ThreadNameWidth) {
  1017. Context->ThreadProfiling.ThreadNameWidth = Length;
  1018. }
  1019. break;
  1020. case ProfilerThreadEventTimeCounter:
  1021. Result = DbgrpReadFromProfilingBuffers(
  1022. &LocalList,
  1023. &(Context->ThreadProfiling.ReferenceTime),
  1024. sizeof(PROFILER_THREAD_TIME_COUNTER),
  1025. TRUE);
  1026. if (Result == FALSE) {
  1027. break;
  1028. }
  1029. break;
  1030. default:
  1031. DbgOut("Unrecognized thread profiling event %d received.\n",
  1032. EventType);
  1033. DbgrpReadFromProfilingBuffers(&LocalList,
  1034. &EventType,
  1035. 1,
  1036. TRUE);
  1037. break;
  1038. }
  1039. } else {
  1040. if (EventType >= ProfilerThreadEventSchedulerMax) {
  1041. DbgOut("Got unknown context swap event, type %d.\n",
  1042. EventType);
  1043. DbgrpReadFromProfilingBuffers(&LocalList,
  1044. &EventType,
  1045. 1,
  1046. TRUE);
  1047. }
  1048. //
  1049. // It's a context switch event. Add it to that array if it's
  1050. // large enough.
  1051. //
  1052. ContextSwap = malloc(sizeof(CONTEXT_SWAP_EVENT));
  1053. if (ContextSwap != NULL) {
  1054. ContextSwap->Processor = ProfilerData->Processor;
  1055. Result = DbgrpReadFromProfilingBuffers(
  1056. &LocalList,
  1057. &(ContextSwap->Event),
  1058. sizeof(PROFILER_CONTEXT_SWAP),
  1059. TRUE);
  1060. if (Result == FALSE) {
  1061. free(ContextSwap);
  1062. break;
  1063. }
  1064. Result = DbgrpPointerArrayAddElement(
  1065. Context->ThreadProfiling.ContextSwaps,
  1066. ContextSwap);
  1067. if (Result == FALSE) {
  1068. free(ContextSwap);
  1069. }
  1070. }
  1071. }
  1072. if (Result == FALSE) {
  1073. break;
  1074. }
  1075. }
  1076. //
  1077. // If there are any buffers left on the local list, put them back on the
  1078. // main list.
  1079. //
  1080. if (LIST_EMPTY(&LocalList) == FALSE) {
  1081. AcquireDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  1082. while (LIST_EMPTY(&LocalList) == FALSE) {
  1083. CurrentEntry = LocalList.Previous;
  1084. LIST_REMOVE(CurrentEntry);
  1085. INSERT_AFTER(CurrentEntry,
  1086. &(Context->ThreadProfiling.StatisticsListHead));
  1087. }
  1088. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  1089. }
  1090. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsLock);
  1091. return;
  1092. }
  1093. VOID
  1094. DbgrpClearThreadProfilingData (
  1095. PDEBUGGER_CONTEXT Context
  1096. )
  1097. /*++
  1098. Routine Description:
  1099. This routine erases all thread profiling data.
  1100. Arguments:
  1101. Context - Supplies a pointer to the application context.
  1102. Return Value:
  1103. None.
  1104. --*/
  1105. {
  1106. //
  1107. // Destroy processed entries.
  1108. //
  1109. AcquireDebuggerLock(Context->ThreadProfiling.StatisticsLock);
  1110. if (Context->ThreadProfiling.ContextSwaps != NULL) {
  1111. DbgrpDestroyPointerArray(Context->ThreadProfiling.ContextSwaps, free);
  1112. Context->ThreadProfiling.ContextSwaps = DbgrpCreatePointerArray(0);
  1113. }
  1114. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsLock);
  1115. //
  1116. // Destroy unprocessed entries.
  1117. //
  1118. AcquireDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  1119. DbgrpDestroyProfilerDataList(
  1120. &(Context->ThreadProfiling.StatisticsListHead));
  1121. ReleaseDebuggerLock(Context->ThreadProfiling.StatisticsListLock);
  1122. return;
  1123. }
  1124. PULONG
  1125. DbgrpCreateThreadIdArray (
  1126. PSTR *Arguments,
  1127. ULONG ArgumentCount
  1128. )
  1129. /*++
  1130. Routine Description:
  1131. This routine implements a helper function that converts an array of strings
  1132. into an array of thread IDs.
  1133. Arguments:
  1134. Arguments - Supplies an array of strings containing the arguments.
  1135. ArgumentCount - Supplies the number of arguments in the Arguments array.
  1136. Return Value:
  1137. Returns an array of thread IDs on success.
  1138. NULL on failure.
  1139. --*/
  1140. {
  1141. PSTR AfterScan;
  1142. ULONG Index;
  1143. LONG Integer;
  1144. PULONG IntegerArray;
  1145. IntegerArray = malloc(ArgumentCount * sizeof(ULONG));
  1146. if (IntegerArray == NULL) {
  1147. return NULL;
  1148. }
  1149. memset(IntegerArray, 0, ArgumentCount * sizeof(ULONG));
  1150. for (Index = 0; Index < ArgumentCount; Index += 1) {
  1151. Integer = strtoul(Arguments[Index], &AfterScan, 0);
  1152. if (AfterScan == Arguments[Index]) {
  1153. DbgOut("Error: Invalid thread ID '%s'.\n", Arguments[Index]);
  1154. free(IntegerArray);
  1155. return NULL;
  1156. }
  1157. IntegerArray[Index] = Integer;
  1158. }
  1159. return IntegerArray;
  1160. }
  1161. PSTR
  1162. DbgpGetProcessName (
  1163. PDEBUGGER_CONTEXT Context,
  1164. ULONG ProcessId,
  1165. PSTR NumericBuffer,
  1166. ULONG NumericBufferSize
  1167. )
  1168. /*++
  1169. Routine Description:
  1170. This routine gets the name of a given process. If the name cannot be found,
  1171. the number will be converted to a string.
  1172. Arguments:
  1173. Context - Supplies a pointer to the application context.
  1174. ProcessId - Supplies the process ID to convert to a name.
  1175. NumericBuffer - Supplies a pointer to a buffer to use to create the string
  1176. if a name could not be found.
  1177. NumericBufferSize - Supplies the size of the numeric buffer in bytes.
  1178. Return Value:
  1179. Returns the name of the process, returning either a found name or the
  1180. numeric buffer. Either way, the caller does not need to free this buffer.
  1181. --*/
  1182. {
  1183. ULONGLONG Index;
  1184. PPROFILER_THREAD_NEW_PROCESS Process;
  1185. PPOINTER_ARRAY Processes;
  1186. Processes = Context->ThreadProfiling.Processes;
  1187. if (Processes != NULL) {
  1188. for (Index = 0; Index < Processes->Size; Index += 1) {
  1189. Process = Processes->Elements[Index];
  1190. if (Process->ProcessId == ProcessId) {
  1191. if (RtlStringLength(Process->Name) != 0) {
  1192. return Process->Name;
  1193. }
  1194. }
  1195. }
  1196. }
  1197. snprintf(NumericBuffer, NumericBufferSize, "%d", ProcessId);
  1198. return NumericBuffer;
  1199. }
  1200. PSTR
  1201. DbgrpGetThreadName (
  1202. PDEBUGGER_CONTEXT Context,
  1203. ULONG ThreadId,
  1204. PSTR NumericBuffer,
  1205. ULONG NumericBufferSize
  1206. )
  1207. /*++
  1208. Routine Description:
  1209. This routine gets the name of a given thread. If the name cannot be found,
  1210. the number will be converted to a string.
  1211. Arguments:
  1212. Context - Supplies a pointer to the application context.
  1213. ThreadId - Supplies the thread ID to convert to a name.
  1214. NumericBuffer - Supplies a pointer to a buffer to use to create the string
  1215. if a name could not be found.
  1216. NumericBufferSize - Supplies the size of the numeric buffer in bytes.
  1217. Return Value:
  1218. Returns the name of the thread, returning either a found name or the
  1219. numeric buffer. Either way, the caller does not need to free this buffer.
  1220. --*/
  1221. {
  1222. ULONGLONG Index;
  1223. PPROFILER_THREAD_NEW_THREAD Thread;
  1224. PPOINTER_ARRAY Threads;
  1225. Threads = Context->ThreadProfiling.Threads;
  1226. if (Threads != NULL) {
  1227. for (Index = 0; Index < Threads->Size; Index += 1) {
  1228. Thread = Threads->Elements[Index];
  1229. if (Thread->ThreadId == ThreadId) {
  1230. if (RtlStringLength(Thread->Name) != 0) {
  1231. return Thread->Name;
  1232. }
  1233. }
  1234. }
  1235. }
  1236. snprintf(NumericBuffer, NumericBufferSize, "%d", ThreadId);
  1237. return NumericBuffer;
  1238. }
  1239. int
  1240. DbgrpCompareContextSwapsByTimeAscending (
  1241. const void *LeftPointer,
  1242. const void *RightPointer
  1243. )
  1244. /*++
  1245. Routine Description:
  1246. This routine compares the timestamps of two pointers to pointers to
  1247. context swap events.
  1248. Arguments:
  1249. LeftPointer - Supplies a pointer to the left element of pointer array.
  1250. RightPointer - Supplies a pointer to the right element of the pointer array.
  1251. Return Value:
  1252. -1 if the left timestamp is less than the right.
  1253. 0 if the left timestamp is equal to the right.
  1254. 1 if the left timestamp is greater than the right.
  1255. --*/
  1256. {
  1257. PCONTEXT_SWAP_EVENT Left;
  1258. PCONTEXT_SWAP_EVENT Right;
  1259. Left = *((PCONTEXT_SWAP_EVENT *)LeftPointer);
  1260. Right = *((PCONTEXT_SWAP_EVENT *)RightPointer);
  1261. if (Left->Event.TimeCount < Right->Event.TimeCount) {
  1262. return -1;
  1263. }
  1264. if (Left->Event.TimeCount > Right->Event.TimeCount) {
  1265. return 1;
  1266. }
  1267. //
  1268. // For events whose time counts are the same, sort by processor.
  1269. //
  1270. if (Left->Processor < Right->Processor) {
  1271. return -1;
  1272. }
  1273. if (Left->Processor > Right->Processor) {
  1274. return 1;
  1275. }
  1276. return 0;
  1277. }
  1278. BOOL
  1279. DbgrpReadFromProfilingBuffers (
  1280. PLIST_ENTRY ListHead,
  1281. PVOID Buffer,
  1282. ULONG Size,
  1283. BOOL Consume
  1284. )
  1285. /*++
  1286. Routine Description:
  1287. This routine reads from the profiling data buffers, freeing and consuming
  1288. data as it goes.
  1289. Arguments:
  1290. ListHead - Supplies a pointer to the head of the list of entries.
  1291. Buffer - Supplies a pointer where the read data will be returned.
  1292. Size - Supplies the number of bytes to consume.
  1293. Consume - Supplies a boolean indicating if the bytes should be consumed
  1294. out of the profiling buffers (TRUE) or just peeked at (FALSE).
  1295. Return Value:
  1296. TRUE if the full amount could be read.
  1297. FALSE if the full amount was not available in the buffers. The buffers will
  1298. not be advanced if this is the case.
  1299. --*/
  1300. {
  1301. ULONG BytesRead;
  1302. PLIST_ENTRY CurrentEntry;
  1303. PPROFILER_DATA_ENTRY Entry;
  1304. ULONG SizeThisRound;
  1305. //
  1306. // Loop once performing the read.
  1307. //
  1308. BytesRead = 0;
  1309. CurrentEntry = ListHead->Next;
  1310. while (CurrentEntry != ListHead) {
  1311. Entry = LIST_VALUE(CurrentEntry, PROFILER_DATA_ENTRY, ListEntry);
  1312. assert(Entry->Offset <= Entry->DataSize);
  1313. SizeThisRound = Entry->DataSize - Entry->Offset;
  1314. if (SizeThisRound > Size - BytesRead) {
  1315. SizeThisRound = Size - BytesRead;
  1316. }
  1317. RtlCopyMemory(Buffer + BytesRead,
  1318. Entry->Data + Entry->Offset,
  1319. SizeThisRound);
  1320. BytesRead += SizeThisRound;
  1321. if (BytesRead == Size) {
  1322. break;
  1323. }
  1324. CurrentEntry = CurrentEntry->Next;
  1325. }
  1326. if (BytesRead != Size) {
  1327. return FALSE;
  1328. }
  1329. if (Consume == FALSE) {
  1330. return TRUE;
  1331. }
  1332. //
  1333. // Loop again consuming the buffer.
  1334. //
  1335. BytesRead = 0;
  1336. CurrentEntry = ListHead->Next;
  1337. while (CurrentEntry != ListHead) {
  1338. Entry = LIST_VALUE(CurrentEntry, PROFILER_DATA_ENTRY, ListEntry);
  1339. assert(Entry->Offset <= Entry->DataSize);
  1340. SizeThisRound = Entry->DataSize - Entry->Offset;
  1341. if (SizeThisRound > Size - BytesRead) {
  1342. SizeThisRound = Size - BytesRead;
  1343. }
  1344. BytesRead += SizeThisRound;
  1345. Entry->Offset += SizeThisRound;
  1346. if (BytesRead == Size) {
  1347. break;
  1348. }
  1349. assert(Entry->Offset == Entry->DataSize);
  1350. CurrentEntry = CurrentEntry->Next;
  1351. LIST_REMOVE(&(Entry->ListEntry));
  1352. free(Entry->Data);
  1353. free(Entry);
  1354. }
  1355. return TRUE;
  1356. }
  1357. PPOINTER_ARRAY
  1358. DbgrpCreatePointerArray (
  1359. ULONGLONG InitialCapacity
  1360. )
  1361. /*++
  1362. Routine Description:
  1363. This routine creates a resizeable pointer array.
  1364. Arguments:
  1365. InitialCapacity - Supplies the initial number of elements to support. Supply
  1366. 0 to use a default value.
  1367. Return Value:
  1368. Returns a pointer to the array on success.
  1369. NULL on allocation failure.
  1370. --*/
  1371. {
  1372. PPOINTER_ARRAY Array;
  1373. Array = malloc(sizeof(POINTER_ARRAY));
  1374. if (Array == NULL) {
  1375. return NULL;
  1376. }
  1377. memset(Array, 0, sizeof(POINTER_ARRAY));
  1378. if (InitialCapacity != 0) {
  1379. Array->Elements = malloc(InitialCapacity * sizeof(PVOID));
  1380. if (Array->Elements == NULL) {
  1381. free(Array);
  1382. return NULL;
  1383. }
  1384. memset(Array->Elements, 0, InitialCapacity * sizeof(PVOID));
  1385. Array->Capacity = InitialCapacity;
  1386. }
  1387. return Array;
  1388. }
  1389. VOID
  1390. DbgrpDestroyPointerArray (
  1391. PPOINTER_ARRAY Array,
  1392. PPOINTER_ARRAY_ITERATE_ROUTINE DestroyRoutine
  1393. )
  1394. /*++
  1395. Routine Description:
  1396. This routine destroys a resizeable pointer array.
  1397. Arguments:
  1398. Array - Supplies a pointer to the array to destroy.
  1399. DestroyRoutine - Supplies an optional pointer to a function to call on
  1400. each element to destroy it.
  1401. Return Value:
  1402. None.
  1403. --*/
  1404. {
  1405. ULONGLONG Index;
  1406. if (Array == NULL) {
  1407. return;
  1408. }
  1409. if (Array->Elements != NULL) {
  1410. if (DestroyRoutine != NULL) {
  1411. for (Index = 0; Index < Array->Size; Index += 1) {
  1412. DestroyRoutine(Array->Elements[Index]);
  1413. }
  1414. }
  1415. free(Array->Elements);
  1416. }
  1417. free(Array);
  1418. return;
  1419. }
  1420. BOOL
  1421. DbgrpPointerArrayAddElement (
  1422. PPOINTER_ARRAY Array,
  1423. PVOID Element
  1424. )
  1425. /*++
  1426. Routine Description:
  1427. This routine adds an element to the end of a pointer array.
  1428. Arguments:
  1429. Array - Supplies a pointer to the array.
  1430. Element - Supplies the element to add.
  1431. Return Value:
  1432. TRUE if the element was successfully added.
  1433. FALSE on reallocation failure.
  1434. --*/
  1435. {
  1436. PVOID *NewBuffer;
  1437. ULONGLONG NewCapacity;
  1438. if (Array->Size == Array->Capacity) {
  1439. NewCapacity = Array->Capacity * 2;
  1440. if (NewCapacity < INITIAL_POINTER_ARRAY_CAPACITY) {
  1441. NewCapacity = INITIAL_POINTER_ARRAY_CAPACITY;
  1442. }
  1443. NewBuffer = realloc(Array->Elements, NewCapacity * sizeof(PVOID));
  1444. if (NewBuffer == NULL) {
  1445. return FALSE;
  1446. }
  1447. memset(NewBuffer + Array->Size,
  1448. 0,
  1449. (NewCapacity - Array->Size) * sizeof(PVOID));
  1450. Array->Elements = NewBuffer;
  1451. Array->Capacity = NewCapacity;
  1452. }
  1453. assert(Array->Elements != NULL);
  1454. Array->Elements[Array->Size] = Element;
  1455. Array->Size += 1;
  1456. return TRUE;
  1457. }
  1458. VOID
  1459. DbgrpCalculateDuration (
  1460. ULONGLONG Duration,
  1461. ULONGLONG Frequency,
  1462. PULONGLONG TimeDuration,
  1463. PSTR *Units,
  1464. PBOOL TimesTen
  1465. )
  1466. /*++
  1467. Routine Description:
  1468. This routine computes the proper units of time for the given counter ticks.
  1469. Arguments:
  1470. Duration - Supplies the duration in ticks.
  1471. Frequency - Supplies the frequency of the timer in ticks per second.
  1472. TimeDuration - Supplies a pointer where the duration in units of time will
  1473. be returned.
  1474. Units - Supplies a pointer where a constant string will be returned
  1475. representing the units. The caller does not need to free this memory.
  1476. TimesTen - Supplies a pointer where a boolean will be returned indicating
  1477. whether the returned duration is multiplied by ten so the tenths unit
  1478. can be displayed.
  1479. Return Value:
  1480. None.
  1481. --*/
  1482. {
  1483. *TimesTen = FALSE;
  1484. *Units = "";
  1485. if (Frequency != 0) {
  1486. if ((Duration / Frequency) >= 10) {
  1487. Duration = Duration / Frequency;
  1488. *Units = "s";
  1489. } else {
  1490. Duration = (Duration * 1000000000ULL) / Frequency;
  1491. *Units = "ns";
  1492. if (Duration > 1000) {
  1493. Duration = Duration / 100;
  1494. *Units = "us";
  1495. *TimesTen = TRUE;
  1496. if (Duration > 10000) {
  1497. Duration = Duration / 1000;
  1498. *Units = "ms";
  1499. if (Duration > 10000) {
  1500. Duration = Duration / 1000;
  1501. *Units = "s";
  1502. }
  1503. }
  1504. }
  1505. }
  1506. }
  1507. if ((*TimesTen != FALSE) && (Duration > 100)) {
  1508. Duration /= 10UL;
  1509. *TimesTen = FALSE;
  1510. }
  1511. *TimeDuration = Duration;
  1512. return;
  1513. }
  1514. VOID
  1515. DbgrpDestroyBlockingQueue (
  1516. PVOID Queue
  1517. )
  1518. /*++
  1519. Routine Description:
  1520. This routine destroys a profiler blocking queue.
  1521. Arguments:
  1522. Queue - Supplies a pointer to the queue to destroy.
  1523. Return Value:
  1524. None.
  1525. --*/
  1526. {
  1527. PPROFILER_BLOCKING_QUEUE BlockingQueue;
  1528. BlockingQueue = Queue;
  1529. if (BlockingQueue->ThreadList != NULL) {
  1530. DbgrpDestroyPointerArray(BlockingQueue->ThreadList, free);
  1531. }
  1532. free(BlockingQueue);
  1533. return;
  1534. }