profthrd.c 52 KB

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