dbgrprof.c 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. dbgrprof.c
  5. Abstract:
  6. This module implements support for monitoring the debuggee's profiling.
  7. Author:
  8. Chris Stevens 11-Jul-2013
  9. Environment:
  10. Kernel
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "dbgrtl.h"
  16. #include <minoca/debug/spproto.h>
  17. #include <minoca/lib/im.h>
  18. #include <minoca/debug/dbgext.h>
  19. #include "symbols.h"
  20. #include "dbgapi.h"
  21. #include "dbgsym.h"
  22. #include "dbgrprof.h"
  23. #include "dbgprofp.h"
  24. #include "dbgrcomm.h"
  25. #include "console.h"
  26. #include <assert.h>
  27. #include <errno.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. //
  32. // ---------------------------------------------------------------- Definitions
  33. //
  34. //
  35. // Defines the indent length (in characters) of each level of the console
  36. // output of profiler stack data.
  37. //
  38. #define PROFILER_STACK_INDENT_LENGTH 2
  39. //
  40. // Define flags for profiler data entries.
  41. //
  42. #define PROFILER_DATA_FLAGS_MEMORY_SENTINEL 0x1
  43. #define PROFILER_USAGE \
  44. "Usage: profiler <type> [options...]\n" \
  45. "Valid Types:\n" \
  46. " stack - Samples the execution call stack at a regular interval.\n" \
  47. " memory - Displays kernel memory pool data.\n" \
  48. " thread - Displays kernel thread information.\n" \
  49. " help - Display this help.\n" \
  50. "Try 'profiler <type> help' for help with a specific profiling type.\n" \
  51. "Note that profiling must be activated on the target for data to be \n" \
  52. "received.\n\n"
  53. #define STACK_PROFILER_USAGE \
  54. "Usage: profiler stack <command> [options...]\n" \
  55. "This command works with periodic stack trace data sent from the target.\n"\
  56. "Valid commands are:\n" \
  57. " start - Begin displaying stack profiling data in the UI. Note that \n" \
  58. " stack-based profiling must be activated in the target.\n" \
  59. " stop - Stop displaying stack profiling data in the UI. If profiling \n"\
  60. " is still activated in the target then data collection will \n" \
  61. " continue to occur.\n" \
  62. " clear - Delete all historical data stored in the debugger.\n" \
  63. " dump - Write the stack profiling data out to the debugger command \n" \
  64. " console.\n" \
  65. " threshold <percentage> - Set the threshold as a percentage of total \n" \
  66. " hits that a stack entry must achieve to be printed out in \n" \
  67. " the dump. This is useful for limiting results to only those \n" \
  68. " that dominate the sampling.\n" \
  69. " help - Display this help.\n\n"
  70. #define MEMORY_PROFILER_USAGE \
  71. "Usage: profiler memory <command> [options...]\n" \
  72. "This command works with memory statistics sent periodically from the \n" \
  73. "target. Valid commands are:\n" \
  74. " start - Begin displaying memory profiling data in the UI. Note that \n" \
  75. " memory profiling must be activated in the target as well.\n" \
  76. " delta - Begin displaying memory profiling data in the UI as a\n" \
  77. " difference from the current snap of memory information. \n" \
  78. " Values that are not different from the current snap will \n" \
  79. " not be displayed.\n" \
  80. " stop - Stop displaying memory profiling data in the UI. Data may \n" \
  81. " still be collected if activated in the target.\n" \
  82. " clear - Delete all historical data stored in the debugger.\n" \
  83. " dump - Write the memory profiling data out to the debugger command \n" \
  84. " console.\n" \
  85. " threshold <activecount> - Set the minimum threshold of active\n" \
  86. " allocations that must be reached for an allocation to be\n" \
  87. " displayed. This is useful for weeding out unimportant data.\n" \
  88. //
  89. // ------------------------------------------------------ Data Type Definitions
  90. //
  91. //
  92. // ----------------------------------------------- Internal Function Prototypes
  93. //
  94. PSTACK_DATA_ENTRY
  95. DbgrpCreateStackEntry (
  96. PDEBUGGER_CONTEXT Context,
  97. PSTACK_DATA_ENTRY Parent,
  98. ULONGLONG Address
  99. );
  100. VOID
  101. DbgrpInsertStackData (
  102. PSTACK_DATA_ENTRY Parent,
  103. PSTACK_DATA_ENTRY Child
  104. );
  105. VOID
  106. DbgrpPrintProfilerStackData (
  107. PSTACK_DATA_ENTRY Root,
  108. ULONG Threshold
  109. );
  110. PMEMORY_POOL_ENTRY
  111. DbgrpGetMemoryPoolEntry (
  112. PLIST_ENTRY PoolListHead,
  113. PROFILER_MEMORY_TYPE ProfilerMemoryType
  114. );
  115. PPROFILER_MEMORY_POOL_TAG_STATISTIC
  116. DbgrpGetTagStatistics (
  117. PMEMORY_POOL_ENTRY MemoryPoolEntry,
  118. ULONG Tag
  119. );
  120. INT
  121. DbgrpDispatchStackProfilerCommand (
  122. PDEBUGGER_CONTEXT Context,
  123. PSTR *Arguments,
  124. ULONG ArgumentCount
  125. );
  126. INT
  127. DbgrpDispatchMemoryProfilerCommand (
  128. PDEBUGGER_CONTEXT Context,
  129. PSTR *Arguments,
  130. ULONG ArgumentCount
  131. );
  132. //
  133. // -------------------------------------------------------------------- Globals
  134. //
  135. //
  136. // Store a global containing the debugger context, assumed to be singular. This
  137. // is pretty awful and goes against the idea of the debugger context, but
  138. // many of these functions are called back from UI window procedures and timer
  139. // callbacks.
  140. //
  141. PDEBUGGER_CONTEXT DbgrProfilerGlobalContext;
  142. //
  143. // ------------------------------------------------------------------ Functions
  144. //
  145. INT
  146. DbgrProfilerInitialize (
  147. PDEBUGGER_CONTEXT Context
  148. )
  149. /*++
  150. Routine Description:
  151. This routine initializes the debugger for profiler data consumption.
  152. Arguments:
  153. Context - Supplies a pointer to the application context.
  154. Return Value:
  155. 0 on success.
  156. Returns an error code on failure.
  157. --*/
  158. {
  159. INT Result;
  160. DbgrProfilerGlobalContext = Context;
  161. Context->ProfilingData.StackListLock = CreateDebuggerLock();
  162. if (Context->ProfilingData.StackListLock == NULL) {
  163. return ENOMEM;
  164. }
  165. Context->ProfilingData.MemoryListLock = CreateDebuggerLock();
  166. if (Context->ProfilingData.MemoryListLock == NULL) {
  167. return ENOMEM;
  168. }
  169. Result = DbgrpInitializeThreadProfiling(Context);
  170. if (Result != 0) {
  171. return Result;
  172. }
  173. INITIALIZE_LIST_HEAD(&(Context->ProfilingData.StackListHead));
  174. INITIALIZE_LIST_HEAD(&(Context->ProfilingData.MemoryListHead));
  175. Context->ProfilingData.MemoryCollectionActive = FALSE;
  176. return 0;
  177. }
  178. VOID
  179. DbgrProfilerDestroy (
  180. PDEBUGGER_CONTEXT Context
  181. )
  182. /*++
  183. Routine Description:
  184. This routine destroys any structures used to consume profiler data.
  185. Arguments:
  186. Context - Supplies a pointer to the application context.
  187. Return Value:
  188. None.
  189. --*/
  190. {
  191. if (Context->ProfilingData.StackListLock != NULL) {
  192. AcquireDebuggerLock(Context->ProfilingData.StackListLock);
  193. DbgrpDestroyProfilerDataList(&(Context->ProfilingData.StackListHead));
  194. ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
  195. DestroyDebuggerLock(Context->ProfilingData.StackListLock);
  196. }
  197. if (Context->ProfilingData.MemoryListLock != NULL) {
  198. AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
  199. DbgrpDestroyProfilerDataList(&(Context->ProfilingData.MemoryListHead));
  200. ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
  201. DestroyDebuggerLock(Context->ProfilingData.MemoryListLock);
  202. }
  203. DbgrpDestroyThreadProfiling(Context);
  204. DbgrDestroyProfilerStackData(Context->ProfilingData.CommandLineStackRoot);
  205. DbgrDestroyProfilerMemoryData(
  206. Context->ProfilingData.CommandLinePoolListHead);
  207. DbgrDestroyProfilerMemoryData(
  208. Context->ProfilingData.CommandLineBaseListHead);
  209. return;
  210. }
  211. VOID
  212. DbgrProcessProfilerNotification (
  213. PDEBUGGER_CONTEXT Context
  214. )
  215. /*++
  216. Routine Description:
  217. This routine processes a profiler notification that the debuggee sends to
  218. the debugger. The routine should collect the profiler data and return as
  219. quickly as possible.
  220. Arguments:
  221. Context - Supplies a pointer to the application context.
  222. Return Value:
  223. None.
  224. --*/
  225. {
  226. PPROFILER_DATA_ENTRY ProfilerData;
  227. PPROFILER_NOTIFICATION ProfilerNotification;
  228. BOOL Result;
  229. ProfilerData = NULL;
  230. //
  231. // Get the profiler notification data out of the current event.
  232. //
  233. ProfilerNotification = Context->CurrentEvent.ProfilerNotification;
  234. //
  235. // If the end packet was received, denoted by the max profiler type, then
  236. // close out this round of data collection.
  237. //
  238. if (ProfilerNotification->Header.Type >= ProfilerDataTypeMax) {
  239. AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
  240. //
  241. // Put the sentinel on the last entry if memory profiling is active.
  242. //
  243. if (Context->ProfilingData.MemoryCollectionActive != FALSE) {
  244. ASSERT(LIST_EMPTY(&(Context->ProfilingData.MemoryListHead)) ==
  245. FALSE);
  246. ProfilerData = LIST_VALUE(
  247. Context->ProfilingData.MemoryListHead.Previous,
  248. PROFILER_DATA_ENTRY,
  249. ListEntry);
  250. ProfilerData->Flags |= PROFILER_DATA_FLAGS_MEMORY_SENTINEL;
  251. Context->ProfilingData.MemoryCollectionActive = FALSE;
  252. }
  253. ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
  254. Result = TRUE;
  255. goto ProcessProfilerNotificationEnd;
  256. }
  257. //
  258. // This is a valid profiler data type. Create a profiler data list element
  259. // and copy this notification's data into the element.
  260. //
  261. ProfilerData = malloc(sizeof(PROFILER_DATA_ENTRY));
  262. if (ProfilerData == NULL) {
  263. Result = FALSE;
  264. goto ProcessProfilerNotificationEnd;
  265. }
  266. ProfilerData->Processor = ProfilerNotification->Header.Processor;
  267. ProfilerData->DataSize = ProfilerNotification->Header.DataSize;
  268. ProfilerData->Offset = 0;
  269. ProfilerData->Data = malloc(ProfilerData->DataSize);
  270. if (ProfilerData->Data == NULL) {
  271. Result = FALSE;
  272. goto ProcessProfilerNotificationEnd;
  273. }
  274. ProfilerData->Flags = 0;
  275. RtlCopyMemory(ProfilerData->Data,
  276. ProfilerNotification->Data,
  277. ProfilerData->DataSize);
  278. //
  279. // Insert the profile data into the correct list.
  280. //
  281. switch (ProfilerNotification->Header.Type) {
  282. case ProfilerDataTypeStack:
  283. //
  284. // Insert the element into the list of stack samples.
  285. //
  286. AcquireDebuggerLock(Context->ProfilingData.StackListLock);
  287. INSERT_BEFORE(&(ProfilerData->ListEntry),
  288. &(Context->ProfilingData.StackListHead));
  289. ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
  290. Result = TRUE;
  291. break;
  292. case ProfilerDataTypeMemory:
  293. //
  294. // Insert the element into the list of memory samples.
  295. //
  296. AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
  297. Context->ProfilingData.MemoryCollectionActive = TRUE;
  298. INSERT_BEFORE(&(ProfilerData->ListEntry),
  299. &(Context->ProfilingData.MemoryListHead));
  300. ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
  301. Result = TRUE;
  302. break;
  303. case ProfilerDataTypeThread:
  304. DbgrpProcessThreadProfilingData(Context, ProfilerData);
  305. Result = TRUE;
  306. break;
  307. default:
  308. DbgOut("Error: Unknown profiler notification type %d.\n",
  309. ProfilerNotification->Header.Type);
  310. Result = FALSE;
  311. break;
  312. }
  313. ProcessProfilerNotificationEnd:
  314. if (Result == FALSE) {
  315. if (ProfilerData != NULL) {
  316. free(ProfilerData);
  317. }
  318. }
  319. return;
  320. }
  321. INT
  322. DbgrDispatchProfilerCommand (
  323. PDEBUGGER_CONTEXT Context,
  324. PSTR *Arguments,
  325. ULONG ArgumentCount
  326. )
  327. /*++
  328. Routine Description:
  329. This routine handles a profiler command.
  330. Arguments:
  331. Context - Supplies a pointer to the application context.
  332. Arguments - Supplies an array of strings containing the arguments.
  333. ArgumentCount - Supplies the number of arguments in the Arguments array.
  334. Return Value:
  335. 0 on success.
  336. Returns an error code on failure.
  337. --*/
  338. {
  339. INT Result;
  340. //
  341. // Currently the profiler supports only one debug context.
  342. //
  343. assert(Context == DbgrProfilerGlobalContext);
  344. if (ArgumentCount < 1) {
  345. DbgOut(PROFILER_USAGE);
  346. return EINVAL;
  347. }
  348. if (strcasecmp(Arguments[0], "stack") == 0) {
  349. Result = DbgrpDispatchStackProfilerCommand(Context,
  350. Arguments,
  351. ArgumentCount);
  352. } else if (strcasecmp(Arguments[0], "memory") == 0) {
  353. Result = DbgrpDispatchMemoryProfilerCommand(Context,
  354. Arguments,
  355. ArgumentCount);
  356. } else if (strcasecmp(Arguments[0], "thread") == 0) {
  357. Result = DbgrpDispatchThreadProfilerCommand(Context,
  358. Arguments,
  359. ArgumentCount);
  360. } else if (strcasecmp(Arguments[0], "help") == 0) {
  361. DbgOut(PROFILER_USAGE);
  362. Result = 0;
  363. } else {
  364. DbgOut("Error: Invalid profiler type '%s'.\n\n", Arguments[0]);
  365. DbgOut(PROFILER_USAGE);
  366. Result = EINVAL;
  367. }
  368. return Result;
  369. }
  370. VOID
  371. DbgrDisplayCommandLineProfilerData (
  372. PROFILER_DATA_TYPE DataType,
  373. PROFILER_DISPLAY_REQUEST DisplayRequest,
  374. ULONG Threshold
  375. )
  376. /*++
  377. Routine Description:
  378. This routine displays the profiler data collected by the core debugging
  379. infrastructure to standard out.
  380. Arguments:
  381. DataType - Supplies the type of profiler data that is to be displayed.
  382. DisplayRequest - Supplies a value requesting a display action, which can
  383. either be to display data once, continually, or to stop continually
  384. displaying data.
  385. Threshold - Supplies the minimum percentage a stack entry hit must be in
  386. order to be displayed.
  387. Return Value:
  388. None.
  389. --*/
  390. {
  391. PDEBUGGER_CONTEXT Context;
  392. BOOL DeltaMode;
  393. PLIST_ENTRY PoolListHead;
  394. PDEBUGGER_PROFILING_DATA ProfilingData;
  395. BOOL Result;
  396. Context = DbgrProfilerGlobalContext;
  397. ProfilingData = &(Context->ProfilingData);
  398. switch (DisplayRequest) {
  399. case ProfilerDisplayOneTime:
  400. case ProfilerDisplayOneTimeThreshold:
  401. //
  402. // Display the profiler stack data once if there is any.
  403. //
  404. switch (DataType) {
  405. case ProfilerDataTypeStack:
  406. Result = DbgrGetProfilerStackData(
  407. &(ProfilingData->CommandLineStackRoot));
  408. if (Result == FALSE) {
  409. DbgOut("Error: There is no valid stack data to display.\n");
  410. return;
  411. }
  412. DbgrPrintProfilerStackData(ProfilingData->CommandLineStackRoot,
  413. Threshold);
  414. break;
  415. //
  416. // Display the profiler memory data if there is any.
  417. //
  418. case ProfilerDataTypeMemory:
  419. Result = DbgrGetProfilerMemoryData(&PoolListHead);
  420. if ((Result == FALSE) &&
  421. (ProfilingData->CommandLinePoolListHead == NULL)) {
  422. DbgOut("Error: There is no valid memory data to display.\n");
  423. return;
  424. }
  425. //
  426. // Always save the latest valid list in case there is no new data
  427. // for the next call.
  428. //
  429. if (Result != FALSE) {
  430. if (ProfilingData->CommandLinePoolListHead !=
  431. ProfilingData->CommandLineBaseListHead) {
  432. DbgrDestroyProfilerMemoryData(
  433. ProfilingData->CommandLinePoolListHead);
  434. }
  435. ProfilingData->CommandLinePoolListHead = PoolListHead;
  436. }
  437. //
  438. // Try to subtract the base line statistics and determine if delta
  439. // mode is enabled.
  440. //
  441. PoolListHead = DbgrSubtractMemoryStatistics(
  442. ProfilingData->CommandLinePoolListHead,
  443. ProfilingData->CommandLineBaseListHead);
  444. if (PoolListHead != ProfilingData->CommandLinePoolListHead) {
  445. DeltaMode = TRUE;
  446. } else {
  447. DeltaMode = FALSE;
  448. }
  449. //
  450. // Print the statistics to the console and destroy the temporary
  451. // list if a delta was displayed.
  452. //
  453. DbgrPrintProfilerMemoryData(PoolListHead, DeltaMode, Threshold);
  454. if (DeltaMode != FALSE) {
  455. DbgrDestroyProfilerMemoryData(PoolListHead);
  456. }
  457. break;
  458. default:
  459. DbgOut("Error: invalid profiler type %d.\n", DataType);
  460. break;
  461. }
  462. break;
  463. case ProfilerDisplayClear:
  464. switch (DataType) {
  465. case ProfilerDataTypeStack:
  466. DbgrDestroyProfilerStackData(ProfilingData->CommandLineStackRoot);
  467. Context->ProfilingData.CommandLineStackRoot = NULL;
  468. break;
  469. default:
  470. DbgOut("Error: invalid profiler type %d for the 'clear' command.\n",
  471. DataType);
  472. break;
  473. }
  474. break;
  475. case ProfilerDisplayStartDelta:
  476. switch (DataType) {
  477. //
  478. // Establish a base memory record to start delta mode.
  479. //
  480. case ProfilerDataTypeMemory:
  481. //
  482. // Use the most recent memory pool statistics if available.
  483. //
  484. DbgrDestroyProfilerMemoryData(
  485. ProfilingData->CommandLineBaseListHead);
  486. if (ProfilingData->CommandLinePoolListHead != NULL) {
  487. ProfilingData->CommandLineBaseListHead =
  488. ProfilingData->CommandLinePoolListHead;
  489. Result = TRUE;
  490. //
  491. // If they are not available, then query for new statistics.
  492. //
  493. } else {
  494. Result = DbgrGetProfilerMemoryData(&PoolListHead);
  495. if (Result == FALSE) {
  496. ProfilingData->CommandLineBaseListHead = NULL;
  497. DbgOut("There is no memory data available to establish a "
  498. "baseline for delta mode.\n");
  499. } else {
  500. ProfilingData->CommandLineBaseListHead = PoolListHead;
  501. ProfilingData->CommandLinePoolListHead = PoolListHead;
  502. }
  503. }
  504. if (Result != FALSE) {
  505. DbgOut("Memory profiler delta mode enabled.\n");
  506. }
  507. break;
  508. default:
  509. DbgOut("Error: invalid profiler type %d for the 'delta' command.\n",
  510. DataType);
  511. break;
  512. }
  513. break;
  514. case ProfilerDisplayStopDelta:
  515. switch (DataType) {
  516. //
  517. // Remove the record of a memory base to stop delta mode.
  518. //
  519. case ProfilerDataTypeMemory:
  520. if (ProfilingData->CommandLineBaseListHead !=
  521. ProfilingData->CommandLinePoolListHead) {
  522. DbgrDestroyProfilerMemoryData(
  523. ProfilingData->CommandLineBaseListHead);
  524. }
  525. ProfilingData->CommandLineBaseListHead = NULL;
  526. DbgOut("Memory profiler delta mode disabled.\n");
  527. break;
  528. default:
  529. DbgOut("Error: invalid profiler type %d for the 'delta' command.\n",
  530. DataType);
  531. break;
  532. }
  533. break;
  534. default:
  535. DbgOut("Error: Invalid profiler display request %d.\n", DisplayRequest);
  536. break;
  537. }
  538. return;
  539. }
  540. BOOL
  541. DbgrGetProfilerStackData (
  542. PSTACK_DATA_ENTRY *StackTreeRoot
  543. )
  544. /*++
  545. Routine Description:
  546. This routine processes and returns any pending profiling stack data. It
  547. will add it to the provided stack tree root. The caller is responsible for
  548. destroying the tree.
  549. Arguments:
  550. StackTreeRoot - Supplies a pointer to a pointer to the root of the stack
  551. data tree. Upon return from the routine it will be updated with all the
  552. newly parsed data. If the root is null, a new root will be allocated.
  553. Return Value:
  554. Returns TRUE when data is successfully returned, or FALSE on failure.
  555. --*/
  556. {
  557. ULONGLONG Address;
  558. PSTACK_DATA_ENTRY AllocatedRoot;
  559. PDEBUGGER_CONTEXT Context;
  560. PSTACK_DATA_ENTRY CurrentEntry;
  561. ULONG CurrentStackLength;
  562. PLIST_ENTRY DataEntry;
  563. ULONG Index;
  564. ULONGLONG Offset;
  565. PSTACK_DATA_ENTRY Parent;
  566. ULONG PointerSize;
  567. PPROFILER_DATA_ENTRY ProfilerData;
  568. BOOL Result;
  569. PSTACK_DATA_ENTRY Root;
  570. ULONG RoutineCount;
  571. PSTACK_DATA_ENTRY StackData;
  572. PLIST_ENTRY StackEntry;
  573. ULONG StackLength;
  574. LIST_ENTRY StackListHead;
  575. assert(StackTreeRoot != NULL);
  576. Context = DbgrProfilerGlobalContext;
  577. AllocatedRoot = NULL;
  578. Result = FALSE;
  579. INITIALIZE_LIST_HEAD(&StackListHead);
  580. //
  581. // If the tree root is NULL, create a new one for the caller.
  582. //
  583. if (*StackTreeRoot == NULL) {
  584. AllocatedRoot = DbgrpCreateStackEntry(DbgrProfilerGlobalContext,
  585. NULL,
  586. 0);
  587. if (AllocatedRoot == NULL) {
  588. Result = FALSE;
  589. goto GetProfilerStackDataEnd;
  590. }
  591. *StackTreeRoot = AllocatedRoot;
  592. }
  593. Root = *StackTreeRoot;
  594. //
  595. // Acquire the profiler lock and copy the head of the stack data list, fix
  596. // up the pointers, and empty the global list. If the list is currently
  597. // empty, just exit.
  598. //
  599. AcquireDebuggerLock(Context->ProfilingData.StackListLock);
  600. if (LIST_EMPTY(&(Context->ProfilingData.StackListHead)) != FALSE) {
  601. Result = TRUE;
  602. ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
  603. goto GetProfilerStackDataEnd;
  604. }
  605. RtlCopyMemory(&StackListHead,
  606. &(Context->ProfilingData.StackListHead),
  607. sizeof(LIST_ENTRY));
  608. StackListHead.Next->Previous = &StackListHead;
  609. StackListHead.Previous->Next = &StackListHead;
  610. INITIALIZE_LIST_HEAD(&(Context->ProfilingData.StackListHead));
  611. ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
  612. //
  613. // Loop through each profiler stack data packet in the list, adding its
  614. // stack entries to the tree of stack data.
  615. //
  616. PointerSize = DbgGetTargetPointerSize(Context);
  617. DataEntry = StackListHead.Next;
  618. while (DataEntry != &StackListHead) {
  619. ProfilerData = LIST_VALUE(DataEntry, PROFILER_DATA_ENTRY, ListEntry);
  620. RoutineCount = ProfilerData->DataSize / PointerSize;
  621. if ((ProfilerData->DataSize % PointerSize) != 0) {
  622. DbgOut("Bad profiler data size %d.\n", ProfilerData->DataSize);
  623. Result = FALSE;
  624. goto GetProfilerStackDataEnd;
  625. }
  626. //
  627. // Run through the data array backwards to parse each stack from the
  628. // root routine.
  629. //
  630. Parent = Root;
  631. CurrentStackLength = 0;
  632. for (Index = RoutineCount; Index > 0; Index -= 1) {
  633. Address = 0;
  634. Offset = (Index - 1) * PointerSize;
  635. RtlCopyMemory(&Address, &(ProfilerData->Data[Offset]), PointerSize);
  636. //
  637. // Every sentinel encountered means that a call stack was
  638. // completely processed.
  639. //
  640. if (IS_PROFILER_DATA_SENTINEL(Address) != FALSE) {
  641. //
  642. // Validate that this was a complete stack. The stack size
  643. // stored in the sentinel marker includes the size of the
  644. // sentinel.
  645. //
  646. StackLength = GET_PROFILER_DATA_SIZE(Address) / PointerSize;
  647. if (CurrentStackLength != (StackLength - 1)) {
  648. DbgOut("Error: Profiler collected incomplete call "
  649. "stack.\n");
  650. Result = FALSE;
  651. goto GetProfilerStackDataEnd;
  652. }
  653. CurrentStackLength = 0;
  654. Root->Count += 1;
  655. Parent = Root;
  656. continue;
  657. }
  658. //
  659. // Look up the call site in the parent's list of children.
  660. //
  661. CurrentEntry = NULL;
  662. StackEntry = Parent->Children.Next;
  663. while (StackEntry != &(Parent->Children)) {
  664. StackData = LIST_VALUE(StackEntry,
  665. STACK_DATA_ENTRY,
  666. SiblingEntry);
  667. if (StackData->Address == Address) {
  668. CurrentEntry = StackData;
  669. break;
  670. }
  671. StackEntry = StackEntry->Next;
  672. }
  673. //
  674. // If there was no match, create a new entry. If this fails,
  675. // just exit returning failure.
  676. //
  677. if (CurrentEntry == NULL) {
  678. CurrentEntry = DbgrpCreateStackEntry(DbgrProfilerGlobalContext,
  679. Parent,
  680. Address);
  681. if (CurrentEntry == NULL) {
  682. DbgOut("Error: Failed to create stack entry.\n");
  683. Result = FALSE;
  684. goto GetProfilerStackDataEnd;
  685. }
  686. }
  687. //
  688. // Account for this match on the current entry, remove it, and
  689. // then insert it back into the stack in order.
  690. //
  691. CurrentEntry->Count += 1;
  692. LIST_REMOVE(&(CurrentEntry->SiblingEntry));
  693. DbgrpInsertStackData(Parent, CurrentEntry);
  694. //
  695. // Move down the stack.
  696. //
  697. Parent = CurrentEntry;
  698. CurrentStackLength += 1;
  699. }
  700. //
  701. // Move on to the next entry.
  702. //
  703. DataEntry = DataEntry->Next;
  704. }
  705. Result = TRUE;
  706. GetProfilerStackDataEnd:
  707. DbgrpDestroyProfilerDataList(&StackListHead);
  708. //
  709. // If the routine failed and allocated the root, destroy the tree.
  710. //
  711. if (Result == FALSE) {
  712. if (AllocatedRoot != NULL) {
  713. DbgrDestroyProfilerStackData(AllocatedRoot);
  714. if (*StackTreeRoot == AllocatedRoot) {
  715. *StackTreeRoot = NULL;
  716. }
  717. }
  718. }
  719. return Result;
  720. }
  721. VOID
  722. DbgrDestroyProfilerStackData (
  723. PSTACK_DATA_ENTRY Root
  724. )
  725. /*++
  726. Routine Description:
  727. This routine destroys a profiler stack data tree.
  728. Arguments:
  729. Root - Supplies a pointer to the root element of the tree.
  730. Return Value:
  731. None.
  732. --*/
  733. {
  734. PLIST_ENTRY CurrentEntry;
  735. PSTACK_DATA_ENTRY StackData;
  736. if (Root == NULL) {
  737. return;
  738. }
  739. //
  740. // Recursively destroy all the children.
  741. //
  742. while (LIST_EMPTY(&(Root->Children)) == FALSE) {
  743. CurrentEntry = Root->Children.Next;
  744. StackData = LIST_VALUE(CurrentEntry, STACK_DATA_ENTRY, SiblingEntry);
  745. DbgrDestroyProfilerStackData(StackData);
  746. }
  747. //
  748. // Now destroy the current root.
  749. //
  750. if (Root->SiblingEntry.Next != NULL) {
  751. LIST_REMOVE(&(Root->SiblingEntry));
  752. }
  753. if (Root->AddressSymbol != NULL) {
  754. free(Root->AddressSymbol);
  755. }
  756. free(Root);
  757. return;
  758. }
  759. VOID
  760. DbgrPrintProfilerStackData (
  761. PSTACK_DATA_ENTRY Root,
  762. ULONG Threshold
  763. )
  764. /*++
  765. Routine Description:
  766. This routine prints profiler stack data to standard out.
  767. Arguments:
  768. Root - Supplies a pointer to the root of the profiler stack data tree.
  769. Threshold - Supplies the minimum percentage a stack entry hit must be in
  770. order to be displayed.
  771. Return Value:
  772. None.
  773. --*/
  774. {
  775. DbgrpPrintProfilerStackData(Root, Threshold);
  776. return;
  777. }
  778. VOID
  779. DbgrProfilerStackEntrySelected (
  780. PSTACK_DATA_ENTRY Root
  781. )
  782. /*++
  783. Routine Description:
  784. This routine is called when a profiler stack data entry is selected by the
  785. user.
  786. Arguments:
  787. Root - Supplies a pointer to the root of the profiler stack data tree.
  788. Return Value:
  789. None.
  790. --*/
  791. {
  792. //
  793. // If an entry is found, highlight the code line associated with the
  794. // selected item. This operation will remove the highlight from the
  795. // previously selected item.
  796. //
  797. if ((Root != NULL) && (Root->Address != 0)) {
  798. DbgrShowSourceAtAddress(DbgrProfilerGlobalContext, Root->Address);
  799. }
  800. return;
  801. }
  802. BOOL
  803. DbgrGetProfilerMemoryData (
  804. PLIST_ENTRY *MemoryPoolListHead
  805. )
  806. /*++
  807. Routine Description:
  808. This routine processes and returns any pending profiling memory data.
  809. Arguments:
  810. MemoryPoolListHead - Supplies a pointer to head of the memory pool list
  811. that is to be populated with the most up to date pool data.
  812. Return Value:
  813. Returns TRUE when data is successfully returned, or FALSE on failure.
  814. --*/
  815. {
  816. ULONG BytesRemaining;
  817. PDEBUGGER_CONTEXT Context;
  818. BYTE *Data;
  819. ULONG DataSize;
  820. LIST_ENTRY LocalListHead;
  821. PLIST_ENTRY MemoryListEntry;
  822. LIST_ENTRY MemoryListHead;
  823. PMEMORY_POOL_ENTRY MemoryPoolEntry;
  824. PLIST_ENTRY NewPoolListHead;
  825. ULONG Offset;
  826. PPROFILER_DATA_ENTRY ProfilerData;
  827. BOOL Result;
  828. ULONG SentinelCount;
  829. ULONG TagCount;
  830. ULONG TagSize;
  831. Context = DbgrProfilerGlobalContext;
  832. Data = NULL;
  833. NewPoolListHead = NULL;
  834. INITIALIZE_LIST_HEAD(&LocalListHead);
  835. INITIALIZE_LIST_HEAD(&MemoryListHead);
  836. //
  837. // Allocate a new pool list head to return to the caller if successful.
  838. //
  839. NewPoolListHead = malloc(sizeof(LIST_ENTRY));
  840. if (NewPoolListHead == NULL) {
  841. Result = FALSE;
  842. goto GetProfilerMemoryDataEnd;
  843. }
  844. INITIALIZE_LIST_HEAD(NewPoolListHead);
  845. //
  846. // Acquire the profiler memory lock and remove all complete memory data
  847. // packets.
  848. //
  849. AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
  850. //
  851. // Do nothing if the list is empty.
  852. //
  853. if (LIST_EMPTY(&(Context->ProfilingData.MemoryListHead)) != FALSE) {
  854. Result = FALSE;
  855. ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
  856. goto GetProfilerMemoryDataEnd;
  857. }
  858. //
  859. // First remove all the data packets.
  860. //
  861. RtlCopyMemory(&LocalListHead,
  862. &(Context->ProfilingData.MemoryListHead),
  863. sizeof(LIST_ENTRY));
  864. LocalListHead.Next->Previous = &LocalListHead;
  865. LocalListHead.Previous->Next = &LocalListHead;
  866. INITIALIZE_LIST_HEAD(&(Context->ProfilingData.MemoryListHead));
  867. //
  868. // Now run backwards through the local list, copying packets back to the
  869. // global list until the first sentinel is encountered.
  870. //
  871. while (LIST_EMPTY(&LocalListHead) == FALSE) {
  872. MemoryListEntry = LocalListHead.Previous;
  873. ProfilerData = LIST_VALUE(MemoryListEntry,
  874. PROFILER_DATA_ENTRY,
  875. ListEntry);
  876. if ((ProfilerData->Flags & PROFILER_DATA_FLAGS_MEMORY_SENTINEL) != 0) {
  877. break;
  878. }
  879. LIST_REMOVE(MemoryListEntry);
  880. INSERT_AFTER(MemoryListEntry, &(Context->ProfilingData.MemoryListHead));
  881. }
  882. ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
  883. Result = TRUE;
  884. //
  885. // If this list is empty, just leave.
  886. //
  887. if (LIST_EMPTY(&LocalListHead) != FALSE) {
  888. Result = FALSE;
  889. goto GetProfilerMemoryDataEnd;
  890. }
  891. //
  892. // Only the most recent memory data is interesting, so out of the list of
  893. // completed memory snapshots, find the start of the last one.
  894. //
  895. SentinelCount = 0;
  896. INITIALIZE_LIST_HEAD(&MemoryListHead);
  897. while (LIST_EMPTY(&LocalListHead) == FALSE) {
  898. MemoryListEntry = LocalListHead.Previous;
  899. ProfilerData = LIST_VALUE(MemoryListEntry,
  900. PROFILER_DATA_ENTRY,
  901. ListEntry);
  902. if ((ProfilerData->Flags & PROFILER_DATA_FLAGS_MEMORY_SENTINEL) != 0) {
  903. SentinelCount += 1;
  904. if (SentinelCount > 1) {
  905. break;
  906. }
  907. }
  908. LIST_REMOVE(MemoryListEntry);
  909. INSERT_AFTER(MemoryListEntry, &MemoryListHead);
  910. }
  911. //
  912. // Release the outdated information.
  913. //
  914. DbgrpDestroyProfilerDataList(&LocalListHead);
  915. //
  916. // Now package the data into what the debugger UI consoles expect. Start
  917. // by pulling all the data into one buffer, it may have been awkwardly
  918. // split across packets.
  919. //
  920. DataSize = 0;
  921. MemoryListEntry = MemoryListHead.Next;
  922. while (MemoryListEntry != &MemoryListHead) {
  923. ProfilerData = LIST_VALUE(MemoryListEntry,
  924. PROFILER_DATA_ENTRY,
  925. ListEntry);
  926. DataSize += ProfilerData->DataSize;
  927. MemoryListEntry = MemoryListEntry->Next;
  928. }
  929. Data = malloc(DataSize);
  930. if (Data == NULL) {
  931. DbgOut("Error: failed to allocate %d bytes for the memory profiler.\n",
  932. DataSize);
  933. Result = FALSE;
  934. goto GetProfilerMemoryDataEnd;
  935. }
  936. Offset = 0;
  937. MemoryListEntry = MemoryListHead.Next;
  938. while (MemoryListEntry != &MemoryListHead) {
  939. ProfilerData = LIST_VALUE(MemoryListEntry,
  940. PROFILER_DATA_ENTRY,
  941. ListEntry);
  942. RtlCopyMemory(&(Data[Offset]),
  943. ProfilerData->Data,
  944. ProfilerData->DataSize);
  945. Offset += ProfilerData->DataSize;
  946. MemoryListEntry = MemoryListEntry->Next;
  947. }
  948. //
  949. // With all the data copied, destroy the list.
  950. //
  951. DbgrpDestroyProfilerDataList(&MemoryListHead);
  952. //
  953. // Now read through the data buffer, translating the byte segments into the
  954. // appropriate structures.
  955. //
  956. Offset = 0;
  957. BytesRemaining = DataSize;
  958. while (BytesRemaining != 0) {
  959. if (BytesRemaining < sizeof(PROFILER_MEMORY_POOL)) {
  960. DbgOut("Error: invalid memory pool data.\n");
  961. Result = FALSE;
  962. goto GetProfilerMemoryDataEnd;
  963. }
  964. //
  965. // Allocate a pool entry in anticipation of a valid data buffer.
  966. //
  967. MemoryPoolEntry = malloc(sizeof(MEMORY_POOL_ENTRY));
  968. if (MemoryPoolEntry == NULL) {
  969. DbgOut("Error: failed to allocate %d bytes for a memory pool "
  970. "entry.\n",
  971. sizeof(MEMORY_POOL_ENTRY));
  972. Result = FALSE;
  973. goto GetProfilerMemoryDataEnd;
  974. }
  975. //
  976. // Copy the memory into the pool entry.
  977. //
  978. RtlCopyMemory(&(MemoryPoolEntry->MemoryPool),
  979. &(Data[Offset]),
  980. sizeof(PROFILER_MEMORY_POOL));
  981. Offset += sizeof(PROFILER_MEMORY_POOL);
  982. BytesRemaining -= sizeof(PROFILER_MEMORY_POOL);
  983. //
  984. // If this is not a pool header, then exit.
  985. //
  986. if (MemoryPoolEntry->MemoryPool.Magic != PROFILER_POOL_MAGIC) {
  987. DbgOut("Error: found 0x%08x when expected pool magic 0x%08x.\n",
  988. MemoryPoolEntry->MemoryPool.Magic,
  989. PROFILER_POOL_MAGIC);
  990. Result = FALSE;
  991. free(MemoryPoolEntry);
  992. goto GetProfilerMemoryDataEnd;
  993. }
  994. //
  995. // Determine the number of tag statistics in this pool and whether or
  996. // not the data buffer is big enough to hold the expected tag data. If
  997. // not, then exit.
  998. //
  999. TagCount = MemoryPoolEntry->MemoryPool.TagCount;
  1000. TagSize = TagCount * sizeof(PROFILER_MEMORY_POOL_TAG_STATISTIC);
  1001. if (BytesRemaining < TagSize) {
  1002. DbgOut("Error: unexpected end of memory data buffer. %d bytes "
  1003. "remaining when expected %d bytes.\n",
  1004. BytesRemaining,
  1005. TagSize);
  1006. Result = FALSE;
  1007. free(MemoryPoolEntry);
  1008. goto GetProfilerMemoryDataEnd;
  1009. }
  1010. //
  1011. // Allocate an array for the tag statistics.
  1012. //
  1013. MemoryPoolEntry->TagStatistics = malloc(TagSize);
  1014. if (MemoryPoolEntry->TagStatistics == NULL) {
  1015. DbgOut("Error: failed to allocate %d bytes for a memory pool "
  1016. "tag statistics.\n",
  1017. TagSize);
  1018. Result = FALSE;
  1019. free(MemoryPoolEntry);
  1020. goto GetProfilerMemoryDataEnd;
  1021. }
  1022. //
  1023. // Copy the tag statistics.
  1024. //
  1025. RtlCopyMemory(MemoryPoolEntry->TagStatistics, &(Data[Offset]), TagSize);
  1026. Offset += TagSize;
  1027. BytesRemaining -= TagSize;
  1028. //
  1029. // Insert this complete pool data into the supplied list head.
  1030. //
  1031. INSERT_BEFORE(&(MemoryPoolEntry->ListEntry), NewPoolListHead);
  1032. }
  1033. *MemoryPoolListHead = NewPoolListHead;
  1034. Result = TRUE;
  1035. GetProfilerMemoryDataEnd:
  1036. if (Result == FALSE) {
  1037. DbgrpDestroyProfilerDataList(&MemoryListHead);
  1038. DbgrpDestroyProfilerDataList(&LocalListHead);
  1039. if (Data != NULL) {
  1040. free(Data);
  1041. }
  1042. if (NewPoolListHead != NULL) {
  1043. DbgrDestroyProfilerMemoryData(NewPoolListHead);
  1044. }
  1045. }
  1046. return Result;
  1047. }
  1048. VOID
  1049. DbgrDestroyProfilerMemoryData (
  1050. PLIST_ENTRY PoolListHead
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. This routine destroys a profiler memory data list.
  1055. Arguments:
  1056. PoolListHead - Supplies a pointer to the head of the memory pool list
  1057. that is to be destroyed.
  1058. Return Value:
  1059. None.
  1060. --*/
  1061. {
  1062. PLIST_ENTRY CurrentEntry;
  1063. PMEMORY_POOL_ENTRY MemoryPoolEntry;
  1064. if (PoolListHead == NULL) {
  1065. return;
  1066. }
  1067. //
  1068. // Destroy each element in the list.
  1069. //
  1070. while (LIST_EMPTY(PoolListHead) == FALSE) {
  1071. CurrentEntry = PoolListHead->Next;
  1072. MemoryPoolEntry = LIST_VALUE(CurrentEntry,
  1073. MEMORY_POOL_ENTRY,
  1074. ListEntry);
  1075. LIST_REMOVE(CurrentEntry);
  1076. if (MemoryPoolEntry->TagStatistics != NULL) {
  1077. free(MemoryPoolEntry->TagStatistics);
  1078. }
  1079. free(MemoryPoolEntry);
  1080. }
  1081. free(PoolListHead);
  1082. return;
  1083. }
  1084. VOID
  1085. DbgrPrintProfilerMemoryData (
  1086. PLIST_ENTRY MemoryPoolListHead,
  1087. BOOL DeltaMode,
  1088. ULONG ActiveCountThreshold
  1089. )
  1090. /*++
  1091. Routine Description:
  1092. This routine prints the statistics from the given memory pool list to the
  1093. debugger console.
  1094. Arguments:
  1095. MemoryPoolListHead - Supplies a pointer to the head of the memory pool
  1096. list.
  1097. DeltaMode - Supplies a boolean indicating whether or not the memory pool
  1098. list represents a delta from a previous point in time.
  1099. ActiveCountThreshold - Supplies the active count threshold. No statistics
  1100. will be displayed for tags with an active count less than this
  1101. threshold.
  1102. Return Value:
  1103. None.
  1104. --*/
  1105. {
  1106. PLIST_ENTRY CurrentEntry;
  1107. LONG DeltaAllocationCount;
  1108. LONG DeltaThreshold;
  1109. ULONG FreePercentage;
  1110. ULONG Index;
  1111. PPROFILER_MEMORY_POOL Pool;
  1112. PMEMORY_POOL_ENTRY PoolEntry;
  1113. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
  1114. CurrentEntry = MemoryPoolListHead->Next;
  1115. while (CurrentEntry != MemoryPoolListHead) {
  1116. PoolEntry = LIST_VALUE(CurrentEntry, MEMORY_POOL_ENTRY, ListEntry);
  1117. Pool = &(PoolEntry->MemoryPool);
  1118. //
  1119. // Print the pool statistics.
  1120. //
  1121. if (Pool->TotalPoolSize != 0) {
  1122. FreePercentage = Pool->FreeListSize * 100 / Pool->TotalPoolSize;
  1123. DbgOut("Pool Type %d, Size %I64xh, %d%% free, "
  1124. "%I64d allocation calls, %I64d free calls, %I64d failed.\n",
  1125. Pool->ProfilerMemoryType,
  1126. Pool->TotalPoolSize,
  1127. FreePercentage,
  1128. Pool->TotalAllocationCalls,
  1129. Pool->TotalFreeCalls,
  1130. Pool->FailedAllocations);
  1131. } else {
  1132. ASSERT(Pool->FreeListSize == 0);
  1133. ASSERT(DeltaMode != FALSE);
  1134. DbgOut("Pool Type %d, Size -, -%% free, "
  1135. "%I64d allocation calls, %I64d free calls, %I64d failed.\n",
  1136. Pool->ProfilerMemoryType,
  1137. Pool->TotalAllocationCalls,
  1138. Pool->TotalFreeCalls,
  1139. Pool->FailedAllocations);
  1140. }
  1141. DbgOut("------------------------------------------------------------"
  1142. "----------------------------\n"
  1143. " Largest Active "
  1144. "Max Active\n"
  1145. "Tag Alloc Active Bytes Max Active Bytes Count "
  1146. " Count Lifetime Alloc\n"
  1147. "------------------------------------------------------------"
  1148. "----------------------------\n");
  1149. //
  1150. // Loop through the tags in the pool, printing statistics for each.
  1151. //
  1152. for (Index = 0; Index < Pool->TagCount; Index += 1) {
  1153. Statistic = &(PoolEntry->TagStatistics[Index]);
  1154. if (DeltaMode == FALSE) {
  1155. //
  1156. // Skip statistics that are below the active count threshold.
  1157. //
  1158. if (Statistic->ActiveAllocationCount < ActiveCountThreshold) {
  1159. continue;
  1160. }
  1161. DbgOut("%c%c%c%c %8xh %16I64xh %16I64xh %8d %8d %16I64xh\n",
  1162. (UCHAR)(Statistic->Tag),
  1163. (UCHAR)(Statistic->Tag >> 8),
  1164. (UCHAR)(Statistic->Tag >> 16),
  1165. (UCHAR)(Statistic->Tag >> 24),
  1166. Statistic->LargestAllocation,
  1167. Statistic->ActiveSize,
  1168. Statistic->LargestActiveSize,
  1169. Statistic->ActiveAllocationCount,
  1170. Statistic->LargestActiveAllocationCount,
  1171. Statistic->LifetimeAllocationSize);
  1172. } else {
  1173. //
  1174. // Honor the threshold, the absolute value of both values must
  1175. // be obtained in delta mode.
  1176. //
  1177. DeltaAllocationCount = (LONG)Statistic->ActiveAllocationCount;
  1178. DeltaThreshold = (LONG)ActiveCountThreshold;
  1179. if (DeltaAllocationCount < 0) {
  1180. DeltaAllocationCount = -DeltaAllocationCount;
  1181. }
  1182. if (DeltaThreshold < 0) {
  1183. DeltaThreshold = -DeltaThreshold;
  1184. }
  1185. if (DeltaAllocationCount < DeltaThreshold) {
  1186. continue;
  1187. }
  1188. //
  1189. // Only print on tags in delta mode if there is data present.
  1190. //
  1191. if ((Statistic->ActiveSize == 0) &&
  1192. (Statistic->ActiveAllocationCount == 0) &&
  1193. (Statistic->LifetimeAllocationSize == 0) &&
  1194. (Statistic->LargestAllocation == 0) &&
  1195. (Statistic->LargestActiveAllocationCount == 0) &&
  1196. (Statistic->LargestActiveSize == 0)) {
  1197. continue;
  1198. }
  1199. DbgOut("%c%c%c%c ",
  1200. (UCHAR)(Statistic->Tag),
  1201. (UCHAR)(Statistic->Tag >> 8),
  1202. (UCHAR)(Statistic->Tag >> 16),
  1203. (UCHAR)(Statistic->Tag >> 24));
  1204. if (Statistic->LargestAllocation != 0) {
  1205. DbgOut("%8xh ", Statistic->LargestAllocation);
  1206. } else {
  1207. DbgOut(" - ");
  1208. }
  1209. if (Statistic->ActiveSize != 0) {
  1210. DbgOut(" %16I64d ", Statistic->ActiveSize);
  1211. } else {
  1212. DbgOut(" - ");
  1213. }
  1214. if (Statistic->LargestActiveSize != 0) {
  1215. DbgOut("%16I64xh ", Statistic->LargestActiveSize);
  1216. } else {
  1217. DbgOut(" - ");
  1218. }
  1219. if (Statistic->ActiveAllocationCount != 0) {
  1220. DbgOut("%8d ", Statistic->ActiveAllocationCount);
  1221. } else {
  1222. DbgOut(" - ");
  1223. }
  1224. if (Statistic->LargestActiveAllocationCount != 0) {
  1225. DbgOut("%8d ", Statistic->LargestActiveAllocationCount);
  1226. } else {
  1227. DbgOut(" - ");
  1228. }
  1229. if (Statistic->LifetimeAllocationSize != 0) {
  1230. DbgOut("%16I64xh\n", Statistic->LifetimeAllocationSize);
  1231. } else {
  1232. DbgOut(" -\n");
  1233. }
  1234. }
  1235. }
  1236. DbgOut("\n");
  1237. CurrentEntry = CurrentEntry->Next;
  1238. }
  1239. return;
  1240. }
  1241. PLIST_ENTRY
  1242. DbgrSubtractMemoryStatistics (
  1243. PLIST_ENTRY CurrentListHead,
  1244. PLIST_ENTRY BaseListHead
  1245. )
  1246. /*++
  1247. Routine Description:
  1248. This routine subtracts the given current memory list from the base memory
  1249. list, returning a list that contains the deltas for memory pool statistics.
  1250. If this routine ever fails, it just returns the current list.
  1251. Arguments:
  1252. CurrentListHead - Supplies a pointer to the head of the current list of
  1253. memory pool data.
  1254. BaseListHead - Supplies a pointer to the head of the base line memory list
  1255. from which the deltas are created.
  1256. Return Value:
  1257. Returns a new memory pool list if possible. If the routine fails or there
  1258. is no base line, then the current memory list is returned.
  1259. --*/
  1260. {
  1261. PPROFILER_MEMORY_POOL BaseMemoryPool;
  1262. PMEMORY_POOL_ENTRY BaseMemoryPoolEntry;
  1263. PPROFILER_MEMORY_POOL_TAG_STATISTIC BaseStatistic;
  1264. PLIST_ENTRY CurrentEntry;
  1265. BOOL DestroyNewList;
  1266. ULONG Index;
  1267. PPROFILER_MEMORY_POOL MemoryPool;
  1268. PMEMORY_POOL_ENTRY MemoryPoolEntry;
  1269. PLIST_ENTRY NewListHead;
  1270. PPROFILER_MEMORY_POOL NewMemoryPool;
  1271. PMEMORY_POOL_ENTRY NewMemoryPoolEntry;
  1272. PROFILER_MEMORY_TYPE ProfilerMemoryType;
  1273. PLIST_ENTRY ResultListHead;
  1274. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
  1275. ULONG TagCount;
  1276. ULONG TagSize;
  1277. assert(CurrentListHead != NULL);
  1278. DestroyNewList = FALSE;
  1279. NewListHead = NULL;
  1280. //
  1281. // Always return the current list head unless the subtraction succeeds.
  1282. //
  1283. ResultListHead = CurrentListHead;
  1284. //
  1285. // Do nothing if the base line statistics are NULL.
  1286. //
  1287. if (BaseListHead == NULL) {
  1288. goto SubtractMemoryStatisticsEnd;
  1289. }
  1290. //
  1291. // Otherwise, create a new list to return the subtracted list.
  1292. //
  1293. NewListHead = malloc(sizeof(LIST_ENTRY));
  1294. if (NewListHead == NULL) {
  1295. goto SubtractMemoryStatisticsEnd;
  1296. }
  1297. INITIALIZE_LIST_HEAD(NewListHead);
  1298. //
  1299. // Loop through the current memory pool list, subtracting the value from
  1300. // the baseline.
  1301. //
  1302. CurrentEntry = CurrentListHead->Next;
  1303. while (CurrentEntry != CurrentListHead) {
  1304. //
  1305. // Get the memory pool entry in the current list.
  1306. //
  1307. MemoryPoolEntry = LIST_VALUE(CurrentEntry,
  1308. MEMORY_POOL_ENTRY,
  1309. ListEntry);
  1310. CurrentEntry = CurrentEntry->Next;
  1311. //
  1312. // Create a new pool entry and a new memory pool.
  1313. //
  1314. NewMemoryPoolEntry = malloc(sizeof(MEMORY_POOL_ENTRY));
  1315. if (NewMemoryPoolEntry == NULL) {
  1316. DestroyNewList = TRUE;
  1317. goto SubtractMemoryStatisticsEnd;
  1318. }
  1319. //
  1320. // Allocate the array for tag statistics.
  1321. //
  1322. MemoryPool = &(MemoryPoolEntry->MemoryPool);
  1323. TagCount = MemoryPool->TagCount;
  1324. TagSize = TagCount * sizeof(PROFILER_MEMORY_POOL_TAG_STATISTIC);
  1325. NewMemoryPoolEntry->TagStatistics = malloc(TagSize);
  1326. if (NewMemoryPoolEntry->TagStatistics == NULL) {
  1327. free(NewMemoryPoolEntry);
  1328. DestroyNewList = TRUE;
  1329. goto SubtractMemoryStatisticsEnd;
  1330. }
  1331. //
  1332. // Copy the the current pool contents.
  1333. //
  1334. NewMemoryPool = &(NewMemoryPoolEntry->MemoryPool);
  1335. RtlCopyMemory(NewMemoryPool, MemoryPool, sizeof(PROFILER_MEMORY_POOL));
  1336. //
  1337. // And the tag statistics.
  1338. //
  1339. RtlCopyMemory(NewMemoryPoolEntry->TagStatistics,
  1340. MemoryPoolEntry->TagStatistics,
  1341. TagSize);
  1342. //
  1343. // Add the new pool entry to the new list.
  1344. //
  1345. INSERT_BEFORE(&(NewMemoryPoolEntry->ListEntry), NewListHead);
  1346. //
  1347. // Find the corresponding memory pool entry in the base list. If it
  1348. // does not exist, then this pool is brand new, don't do any
  1349. // subtraction.
  1350. //
  1351. ProfilerMemoryType = MemoryPool->ProfilerMemoryType;
  1352. BaseMemoryPoolEntry = DbgrpGetMemoryPoolEntry(BaseListHead,
  1353. ProfilerMemoryType);
  1354. if (BaseMemoryPoolEntry == NULL) {
  1355. continue;
  1356. }
  1357. //
  1358. // Now subtract the base statistics from the new copy of the current
  1359. // statistics.
  1360. //
  1361. BaseMemoryPool = &(BaseMemoryPoolEntry->MemoryPool);
  1362. if ((NewMemoryPool->TotalPoolSize == BaseMemoryPool->TotalPoolSize) &&
  1363. (NewMemoryPool->FreeListSize == BaseMemoryPool->FreeListSize)) {
  1364. NewMemoryPool->TotalPoolSize = 0;
  1365. NewMemoryPool->FreeListSize = 0;
  1366. }
  1367. NewMemoryPool->FailedAllocations -= BaseMemoryPool->FailedAllocations;
  1368. NewMemoryPool->TotalFreeCalls -= BaseMemoryPool->TotalFreeCalls;
  1369. NewMemoryPool->TotalAllocationCalls -=
  1370. BaseMemoryPool->TotalAllocationCalls;
  1371. //
  1372. // Loop through the tag statistics and subtract the base statsitics.
  1373. //
  1374. for (Index = 0; Index < TagCount; Index += 1) {
  1375. Statistic = &(NewMemoryPoolEntry->TagStatistics[Index]);
  1376. //
  1377. // Find the corresponding memory pool entry in the base list.
  1378. //
  1379. BaseStatistic = DbgrpGetTagStatistics(BaseMemoryPoolEntry,
  1380. Statistic->Tag);
  1381. if (BaseStatistic == NULL) {
  1382. continue;
  1383. }
  1384. //
  1385. // Subtract the base statistics from the current statistics.
  1386. //
  1387. if (Statistic->LargestAllocation ==
  1388. BaseStatistic->LargestAllocation) {
  1389. Statistic->LargestAllocation = 0;
  1390. }
  1391. if (Statistic->LargestActiveSize ==
  1392. BaseStatistic->LargestActiveSize) {
  1393. Statistic->LargestActiveSize = 0;
  1394. }
  1395. if (Statistic->LifetimeAllocationSize ==
  1396. BaseStatistic->LifetimeAllocationSize) {
  1397. Statistic->LifetimeAllocationSize = 0;
  1398. }
  1399. if (Statistic->LargestActiveAllocationCount ==
  1400. BaseStatistic->LargestActiveAllocationCount) {
  1401. Statistic->LargestActiveAllocationCount = 0;
  1402. }
  1403. Statistic->ActiveSize -= BaseStatistic->ActiveSize;
  1404. Statistic->ActiveAllocationCount -=
  1405. BaseStatistic->ActiveAllocationCount;
  1406. }
  1407. }
  1408. ResultListHead = NewListHead;
  1409. SubtractMemoryStatisticsEnd:
  1410. if (DestroyNewList != FALSE) {
  1411. DbgrDestroyProfilerMemoryData(NewListHead);
  1412. }
  1413. return ResultListHead;
  1414. }
  1415. VOID
  1416. DbgrpDestroyProfilerDataList (
  1417. PLIST_ENTRY ListHead
  1418. )
  1419. /*++
  1420. Routine Description:
  1421. This routine destroys a profiler data list. It does not destroy the head
  1422. of the list.
  1423. Arguments:
  1424. ListHead - Supplies a pointer to the head of the list that is to be
  1425. destroyed.
  1426. Return Value:
  1427. None.
  1428. --*/
  1429. {
  1430. PLIST_ENTRY DataEntry;
  1431. PPROFILER_DATA_ENTRY ProfilerData;
  1432. assert(ListHead != NULL);
  1433. while (LIST_EMPTY(ListHead) == FALSE) {
  1434. DataEntry = ListHead->Next;
  1435. ProfilerData = LIST_VALUE(DataEntry, PROFILER_DATA_ENTRY, ListEntry);
  1436. LIST_REMOVE(DataEntry);
  1437. free(ProfilerData);
  1438. }
  1439. return;
  1440. }
  1441. //
  1442. // --------------------------------------------------------- Internal Functions
  1443. //
  1444. PSTACK_DATA_ENTRY
  1445. DbgrpCreateStackEntry (
  1446. PDEBUGGER_CONTEXT Context,
  1447. PSTACK_DATA_ENTRY Parent,
  1448. ULONGLONG Address
  1449. )
  1450. /*++
  1451. Routine Description:
  1452. This routine creates a stack entry and inserts it into the parent's list of
  1453. children.
  1454. Arguments:
  1455. Context - Supplies a pointer to the application context.
  1456. Parent - Supplies a pointer to the stack entry's parent.
  1457. Address - Supplies the call site address for this stack entry.
  1458. ReturnAddress - Supplies the return address for this stack entry.
  1459. Return Value:
  1460. Returns a pointer to a stack entry on success, or NULL on failure.
  1461. --*/
  1462. {
  1463. PSTR AddressSymbol;
  1464. BOOL Result;
  1465. PSTACK_DATA_ENTRY StackData;
  1466. AddressSymbol = NULL;
  1467. Result = FALSE;
  1468. //
  1469. // Allocate a new stack data entry and begin filling it in.
  1470. //
  1471. StackData = malloc(sizeof(STACK_DATA_ENTRY));
  1472. if (StackData == NULL) {
  1473. DbgOut("Error: Failed to allocate %d bytes.\n",
  1474. sizeof(STACK_DATA_ENTRY));
  1475. goto CreateStackDataEntryEnd;
  1476. }
  1477. RtlZeroMemory(StackData, sizeof(STACK_DATA_ENTRY));
  1478. INITIALIZE_LIST_HEAD(&(StackData->Children));
  1479. INITIALIZE_LIST_HEAD(&(StackData->SiblingEntry));
  1480. StackData->Address = Address;
  1481. StackData->Parent = Parent;
  1482. assert(StackData->Count == 0);
  1483. assert(StackData->UiHandle == NULL);
  1484. //
  1485. // If the parent is NULL, then this is the root. Just exit.
  1486. //
  1487. if (Parent == NULL) {
  1488. assert(StackData->AddressSymbol == NULL);
  1489. Result = TRUE;
  1490. goto CreateStackDataEntryEnd;
  1491. }
  1492. //
  1493. // Get the name for the stack data entry.
  1494. //
  1495. AddressSymbol = DbgGetAddressSymbol(Context, Address);
  1496. if (AddressSymbol == NULL) {
  1497. DbgOut("Error: failed to get symbol for address 0x%I64x.\n", Address);
  1498. goto CreateStackDataEntryEnd;
  1499. }
  1500. StackData->AddressSymbol = AddressSymbol;
  1501. //
  1502. // Insert this new stack entry into the parent's list of children in order.
  1503. //
  1504. if (Parent != NULL) {
  1505. DbgrpInsertStackData(Parent, StackData);
  1506. }
  1507. Result = TRUE;
  1508. CreateStackDataEntryEnd:
  1509. if (Result == FALSE) {
  1510. if (StackData != NULL) {
  1511. free(StackData);
  1512. StackData = NULL;
  1513. }
  1514. if (AddressSymbol != NULL) {
  1515. free(AddressSymbol);
  1516. }
  1517. }
  1518. return StackData;
  1519. }
  1520. VOID
  1521. DbgrpInsertStackData (
  1522. PSTACK_DATA_ENTRY Parent,
  1523. PSTACK_DATA_ENTRY Child
  1524. )
  1525. /*++
  1526. Routine Description:
  1527. This routine inserts the child into the parent's list of children in the
  1528. correct order.
  1529. Arguments:
  1530. Parent - Supplies a pointer to the parent stack entry.
  1531. Child - Supplies a pointer to the stack entry that is to be inserted.
  1532. Return Value:
  1533. None.
  1534. --*/
  1535. {
  1536. PLIST_ENTRY CurrentEntry;
  1537. PSTACK_DATA_ENTRY StackData;
  1538. //
  1539. // The list of children is already sorted, so just search for the correct
  1540. // location to insert it. Going backwards makes this easier.
  1541. //
  1542. CurrentEntry = Parent->Children.Previous;
  1543. while (CurrentEntry != &(Parent->Children)) {
  1544. StackData = LIST_VALUE(CurrentEntry, STACK_DATA_ENTRY, SiblingEntry);
  1545. if (Child->Count < StackData->Count) {
  1546. INSERT_AFTER(&(Child->SiblingEntry), &(StackData->SiblingEntry));
  1547. return;
  1548. }
  1549. if ((Child->Count == StackData->Count) &&
  1550. (Child->Address > StackData->Address)) {
  1551. INSERT_AFTER(&(Child->SiblingEntry), &(StackData->SiblingEntry));
  1552. return;
  1553. }
  1554. CurrentEntry = CurrentEntry->Previous;
  1555. }
  1556. //
  1557. // It was not inserted, just place it at the beginning.
  1558. //
  1559. INSERT_AFTER(&(Child->SiblingEntry), &(Parent->Children));
  1560. return;
  1561. }
  1562. VOID
  1563. DbgrpPrintProfilerStackData (
  1564. PSTACK_DATA_ENTRY Root,
  1565. ULONG Threshold
  1566. )
  1567. /*++
  1568. Routine Description:
  1569. This routine prints information for the given profiler stack data entry and
  1570. all its children to standard out.
  1571. Arguments:
  1572. Root - Supplies a pointer to the root of the stack data tree.
  1573. Threshold - Supplies the minimum percentage a stack entry hit must be in
  1574. order to be displayed.
  1575. Return Value:
  1576. None.
  1577. --*/
  1578. {
  1579. BOOL AddVerticalBar;
  1580. PSTACK_DATA_ENTRY ChildData;
  1581. PSTR FunctionString;
  1582. PSTR IndentString;
  1583. ULONG IndentStringLength;
  1584. PSTACK_DATA_ENTRY NextEntry;
  1585. ULONG NextPercent;
  1586. ULONG Percent;
  1587. PSTACK_DATA_ENTRY StackData;
  1588. ULONG TotalCount;
  1589. TotalCount = Root->Count;
  1590. //
  1591. // Return if the total count is zero. There is nothing to do.
  1592. //
  1593. if (TotalCount == 0) {
  1594. return;
  1595. }
  1596. //
  1597. // The nodes need to be displayed in depth first order where a parent is
  1598. // displayed before any of its children. So the iteration order is to
  1599. // process the current node, then move to either its first child, its next
  1600. // sibling, or an anscestor's sibling. Of course, only move to nodes if
  1601. // they meet the threshold requirements.
  1602. //
  1603. IndentString = malloc(sizeof(UCHAR));
  1604. IndentStringLength = sizeof(UCHAR);
  1605. *IndentString = '\0';
  1606. StackData = Root;
  1607. do {
  1608. Percent = (StackData->Count * 100) / TotalCount;
  1609. assert(Percent >= Threshold);
  1610. //
  1611. // Display the indent string.
  1612. //
  1613. DbgOut(IndentString);
  1614. //
  1615. // If this element has children, print the appropriate start symbol
  1616. // depending on whether or not the element has children. Don't worry
  1617. // about whether or not the children meet the threshold; either way
  1618. // this serves as an indication that there are children.
  1619. //
  1620. if (LIST_EMPTY(&(StackData->Children)) == FALSE) {
  1621. DbgOut(" +");
  1622. } else {
  1623. DbgOut(" -");
  1624. }
  1625. //
  1626. // Print the stack entry's information.
  1627. //
  1628. if (StackData->AddressSymbol == NULL) {
  1629. FunctionString = "Root";
  1630. } else {
  1631. FunctionString = StackData->AddressSymbol;
  1632. }
  1633. DbgOut("%s: %d%%, %d\n", FunctionString, Percent, StackData->Count);
  1634. //
  1635. // Move to the first child if it meets the threshold. The children are
  1636. // always sorted by hit count, so only the first child needs to be
  1637. // checked.
  1638. //
  1639. if (LIST_EMPTY(&(StackData->Children)) == FALSE) {
  1640. ChildData = LIST_VALUE(StackData->Children.Next,
  1641. STACK_DATA_ENTRY,
  1642. SiblingEntry);
  1643. Percent = (ChildData->Count * 100) / TotalCount;
  1644. if (Percent >= Threshold) {
  1645. //
  1646. // Determine the length of the child's indent string.
  1647. //
  1648. IndentStringLength += PROFILER_STACK_INDENT_LENGTH;
  1649. IndentString = realloc(IndentString, IndentStringLength);
  1650. AddVerticalBar = FALSE;
  1651. //
  1652. // If the current node's next sibling will meet the threshold,
  1653. // then a vertical bar needs to be added to the indent.
  1654. //
  1655. if ((StackData->Parent != NULL) &&
  1656. (StackData->SiblingEntry.Next !=
  1657. &(StackData->Parent->Children))) {
  1658. NextEntry = LIST_VALUE(StackData->SiblingEntry.Next,
  1659. STACK_DATA_ENTRY,
  1660. SiblingEntry);
  1661. NextPercent = (NextEntry->Count * 100) / TotalCount;
  1662. if (NextPercent >= Threshold) {
  1663. AddVerticalBar = TRUE;
  1664. }
  1665. }
  1666. IndentString[IndentStringLength - 3] = ' ';
  1667. if (AddVerticalBar == FALSE) {
  1668. IndentString[IndentStringLength - 2] = ' ';
  1669. } else {
  1670. IndentString[IndentStringLength - 2] = '|';
  1671. }
  1672. IndentString[IndentStringLength - 1] = '\0';
  1673. StackData = ChildData;
  1674. continue;
  1675. }
  1676. }
  1677. //
  1678. // The first child didn't exists or didn't meet the threshold. Search
  1679. // until a sibling or ancestor sibling needs to be processed. As the
  1680. // children are in order, only one sibling needs to be checked before
  1681. // moving back up the tree.
  1682. //
  1683. while (StackData != Root) {
  1684. assert(StackData->Parent != NULL);
  1685. if (StackData->SiblingEntry.Next !=
  1686. &(StackData->Parent->Children)) {
  1687. StackData = LIST_VALUE(StackData->SiblingEntry.Next,
  1688. STACK_DATA_ENTRY,
  1689. SiblingEntry);
  1690. Percent = (StackData->Count * 100) / TotalCount;
  1691. if (Percent >= Threshold) {
  1692. break;
  1693. }
  1694. }
  1695. //
  1696. // The sibling didn't work out. So move up the tree and look at
  1697. // the parent's sibling. Update the indent string.
  1698. //
  1699. StackData = StackData->Parent;
  1700. IndentStringLength -= PROFILER_STACK_INDENT_LENGTH;
  1701. IndentString[IndentStringLength - 1] = '\0';
  1702. }
  1703. } while (StackData != Root);
  1704. free(IndentString);
  1705. return;
  1706. }
  1707. PMEMORY_POOL_ENTRY
  1708. DbgrpGetMemoryPoolEntry (
  1709. PLIST_ENTRY PoolListHead,
  1710. PROFILER_MEMORY_TYPE ProfilerMemoryType
  1711. )
  1712. /*++
  1713. Routine Description:
  1714. This routine searches the given memory pool list and returns the entry for
  1715. the given pool type.
  1716. Arguments:
  1717. PoolListHead - Supplies a pointer to the head of the memory pool list to be
  1718. searched.
  1719. ProfilerMemoryType - Supplies the profiler memory type of the memory pool
  1720. being sought.
  1721. Return Value:
  1722. Returns a memory pool entry on success, or NULL on failure.
  1723. --*/
  1724. {
  1725. PLIST_ENTRY CurrentEntry;
  1726. PPROFILER_MEMORY_POOL MemoryPool;
  1727. PMEMORY_POOL_ENTRY MemoryPoolEntry;
  1728. CurrentEntry = PoolListHead->Next;
  1729. while (CurrentEntry != PoolListHead) {
  1730. MemoryPoolEntry = LIST_VALUE(CurrentEntry,
  1731. MEMORY_POOL_ENTRY,
  1732. ListEntry);
  1733. MemoryPool = &(MemoryPoolEntry->MemoryPool);
  1734. if (MemoryPool->ProfilerMemoryType == ProfilerMemoryType) {
  1735. return MemoryPoolEntry;
  1736. }
  1737. CurrentEntry = CurrentEntry->Next;
  1738. }
  1739. return NULL;
  1740. }
  1741. PPROFILER_MEMORY_POOL_TAG_STATISTIC
  1742. DbgrpGetTagStatistics (
  1743. PMEMORY_POOL_ENTRY MemoryPoolEntry,
  1744. ULONG Tag
  1745. )
  1746. /*++
  1747. Routine Description:
  1748. This routine searches the tag statistics in the given memory pool for those
  1749. belonging to the given tag.
  1750. Arguments:
  1751. MemoryPoolEntry - Supplies a pointer to the memory pool entry to be
  1752. searched.
  1753. Tag - Supplies the tag identifying the statistics to be returned.
  1754. Return Value:
  1755. Returns a pointer to memory pool tag statistics on success, or NULL on
  1756. failure.
  1757. --*/
  1758. {
  1759. ULONG Index;
  1760. PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
  1761. ULONG TagCount;
  1762. TagCount = MemoryPoolEntry->MemoryPool.TagCount;
  1763. for (Index = 0; Index < TagCount; Index += 1) {
  1764. Statistic = &(MemoryPoolEntry->TagStatistics[Index]);
  1765. if (Statistic->Tag == Tag) {
  1766. return Statistic;
  1767. }
  1768. }
  1769. return NULL;
  1770. }
  1771. INT
  1772. DbgrpDispatchStackProfilerCommand (
  1773. PDEBUGGER_CONTEXT Context,
  1774. PSTR *Arguments,
  1775. ULONG ArgumentCount
  1776. )
  1777. /*++
  1778. Routine Description:
  1779. This routine handles a stack profiler command.
  1780. Arguments:
  1781. Context - Supplies a pointer to the application context.
  1782. Arguments - Supplies an array of strings containing the arguments.
  1783. ArgumentCount - Supplies the number of arguments in the Arguments array.
  1784. Return Value:
  1785. Returns TRUE on success, or FALSE on failure.
  1786. --*/
  1787. {
  1788. PSTR AdvancedString;
  1789. PROFILER_DISPLAY_REQUEST DisplayRequest;
  1790. LONG Threshold;
  1791. assert(strcasecmp(Arguments[0], "stack") == 0);
  1792. if (ArgumentCount < 2) {
  1793. DbgOut(STACK_PROFILER_USAGE);
  1794. return EINVAL;
  1795. }
  1796. Threshold = 0;
  1797. DisplayRequest = ProfilerDisplayOneTime;
  1798. if (strcasecmp(Arguments[1], "start") == 0) {
  1799. DisplayRequest = ProfilerDisplayStart;
  1800. } else if (strcasecmp(Arguments[1], "stop") == 0) {
  1801. DisplayRequest = ProfilerDisplayStop;
  1802. } else if (strcasecmp(Arguments[1], "clear") == 0) {
  1803. DisplayRequest = ProfilerDisplayClear;
  1804. } else if (strcasecmp(Arguments[1], "dump") == 0) {
  1805. DisplayRequest = ProfilerDisplayOneTime;
  1806. } else if (strcasecmp(Arguments[1], "threshold") == 0) {
  1807. DisplayRequest = ProfilerDisplayOneTimeThreshold;
  1808. if (ArgumentCount < 3) {
  1809. DbgOut("Error: Percentage argument expected.\n");
  1810. return EINVAL;
  1811. }
  1812. Threshold = strtol(Arguments[2], &AdvancedString, 0);
  1813. if (Arguments[2] == AdvancedString) {
  1814. DbgOut("Error: Invalid argument %s. Unable to convert to a valid "
  1815. "threshold value.\n",
  1816. Arguments[2]);
  1817. return EINVAL;
  1818. }
  1819. //
  1820. // The threshold for the stack profiler should only be from 0 to 100.
  1821. //
  1822. if ((Threshold < 0) || (Threshold > 100)) {
  1823. DbgOut("Error: Invalid threshold percentage specified. Valid "
  1824. "values are between 0 and 100.\n");
  1825. return EINVAL;
  1826. }
  1827. } else if (strcasecmp(Arguments[1], "help") == 0) {
  1828. DbgOut(STACK_PROFILER_USAGE);
  1829. return 0;
  1830. } else {
  1831. DbgOut("Error: Unknown stack profiler command '%s'.\n\n", Arguments[1]);
  1832. DbgOut(STACK_PROFILER_USAGE);
  1833. return EINVAL;
  1834. }
  1835. UiDisplayProfilerData(ProfilerDataTypeStack,
  1836. DisplayRequest,
  1837. (ULONG)Threshold);
  1838. return 0;
  1839. }
  1840. INT
  1841. DbgrpDispatchMemoryProfilerCommand (
  1842. PDEBUGGER_CONTEXT Context,
  1843. PSTR *Arguments,
  1844. ULONG ArgumentCount
  1845. )
  1846. /*++
  1847. Routine Description:
  1848. This routine handles a memory profiler command.
  1849. Arguments:
  1850. Context - Supplies a pointer to the application context.
  1851. Arguments - Supplies an array of strings containing the arguments.
  1852. ArgumentCount - Supplies the number of arguments in the Arguments array.
  1853. Return Value:
  1854. 0 on success.
  1855. Returns an error code on failure.
  1856. --*/
  1857. {
  1858. PSTR AdvancedString;
  1859. PROFILER_DISPLAY_REQUEST DisplayRequest;
  1860. LONG Threshold;
  1861. assert(strcasecmp(Arguments[0], "memory") == 0);
  1862. if (ArgumentCount < 2) {
  1863. DbgOut(MEMORY_PROFILER_USAGE);
  1864. return EINVAL;
  1865. }
  1866. Threshold = 0;
  1867. DisplayRequest = ProfilerDisplayOneTime;
  1868. if (strcasecmp(Arguments[1], "start") == 0) {
  1869. DisplayRequest = ProfilerDisplayStart;
  1870. } else if (strcasecmp(Arguments[1], "delta") == 0) {
  1871. DisplayRequest = ProfilerDisplayStartDelta;
  1872. } else if (strcasecmp(Arguments[1], "stop") == 0) {
  1873. DisplayRequest = ProfilerDisplayStop;
  1874. } else if (strcasecmp(Arguments[1], "clear") == 0) {
  1875. DisplayRequest = ProfilerDisplayStopDelta;
  1876. } else if (strcasecmp(Arguments[1], "dump") == 0) {
  1877. DisplayRequest = ProfilerDisplayOneTime;
  1878. } else if (strcasecmp(Arguments[1], "threshold") == 0) {
  1879. DisplayRequest = ProfilerDisplayOneTimeThreshold;
  1880. if (ArgumentCount < 3) {
  1881. DbgOut("Error: Active count threshold argument expected.\n");
  1882. return EINVAL;
  1883. }
  1884. Threshold = strtol(Arguments[2], &AdvancedString, 0);
  1885. if (Arguments[2] == AdvancedString) {
  1886. DbgOut("Error: Invalid argument %s. Unable to convert to a valid "
  1887. "threshold value.\n",
  1888. Arguments[2]);
  1889. return EINVAL;
  1890. }
  1891. } else if (strcasecmp(Arguments[1], "help") == 0) {
  1892. DbgOut(MEMORY_PROFILER_USAGE);
  1893. return 0;
  1894. } else {
  1895. DbgOut("Error: Unknown memory profiler command '%s'.\n\n",
  1896. Arguments[1]);
  1897. DbgOut(MEMORY_PROFILER_USAGE);
  1898. return EINVAL;
  1899. }
  1900. UiDisplayProfilerData(ProfilerDataTypeMemory,
  1901. DisplayRequest,
  1902. (ULONG)Threshold);
  1903. return 0;
  1904. }