dbgrprof.c 69 KB

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