12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- dbgrprof.c
- Abstract:
- This module implements support for monitoring the debuggee's profiling.
- Author:
- Chris Stevens 11-Jul-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "dbgrtl.h"
- #include <minoca/debug/spproto.h>
- #include <minoca/lib/im.h>
- #include <minoca/debug/dbgext.h>
- #include "symbols.h"
- #include "dbgapi.h"
- #include "dbgsym.h"
- #include "dbgrprof.h"
- #include "dbgprofp.h"
- #include "dbgrcomm.h"
- #include "console.h"
- #include <assert.h>
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Defines the indent length (in characters) of each level of the console
- // output of profiler stack data.
- //
- #define PROFILER_STACK_INDENT_LENGTH 2
- //
- // Define flags for profiler data entries.
- //
- #define PROFILER_DATA_FLAGS_MEMORY_SENTINEL 0x1
- #define PROFILER_USAGE \
- "Usage: profiler <type> [options...]\n" \
- "Valid Types:\n" \
- " stack - Samples the execution call stack at a regular interval.\n" \
- " memory - Displays kernel memory pool data.\n" \
- " thread - Displays kernel thread information.\n" \
- " help - Display this help.\n" \
- "Try 'profiler <type> help' for help with a specific profiling type.\n" \
- "Note that profiling must be activated on the target for data to be \n" \
- "received.\n\n"
- #define STACK_PROFILER_USAGE \
- "Usage: profiler stack <command> [options...]\n" \
- "This command works with periodic stack trace data sent from the target.\n"\
- "Valid commands are:\n" \
- " start - Begin displaying stack profiling data in the UI. Note that \n" \
- " stack-based profiling must be activated in the target.\n" \
- " stop - Stop displaying stack profiling data in the UI. If profiling \n"\
- " is still activated in the target then data collection will \n" \
- " continue to occur.\n" \
- " clear - Delete all historical data stored in the debugger.\n" \
- " dump - Write the stack profiling data out to the debugger command \n" \
- " console.\n" \
- " threshold <percentage> - Set the threshold as a percentage of total \n" \
- " hits that a stack entry must achieve to be printed out in \n" \
- " the dump. This is useful for limiting results to only those \n" \
- " that dominate the sampling.\n" \
- " help - Display this help.\n\n"
- #define MEMORY_PROFILER_USAGE \
- "Usage: profiler memory <command> [options...]\n" \
- "This command works with memory statistics sent periodically from the \n" \
- "target. Valid commands are:\n" \
- " start - Begin displaying memory profiling data in the UI. Note that \n" \
- " memory profiling must be activated in the target as well.\n" \
- " delta - Begin displaying memory profiling data in the UI as a\n" \
- " difference from the current snap of memory information. \n" \
- " Values that are not different from the current snap will \n" \
- " not be displayed.\n" \
- " stop - Stop displaying memory profiling data in the UI. Data may \n" \
- " still be collected if activated in the target.\n" \
- " clear - Delete all historical data stored in the debugger.\n" \
- " dump - Write the memory profiling data out to the debugger command \n" \
- " console.\n" \
- " threshold <activecount> - Set the minimum threshold of active\n" \
- " allocations that must be reached for an allocation to be\n" \
- " displayed. This is useful for weeding out unimportant data.\n" \
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- PSTACK_DATA_ENTRY
- DbgrpCreateStackEntry (
- PDEBUGGER_CONTEXT Context,
- PSTACK_DATA_ENTRY Parent,
- ULONGLONG Address
- );
- VOID
- DbgrpInsertStackData (
- PSTACK_DATA_ENTRY Parent,
- PSTACK_DATA_ENTRY Child
- );
- VOID
- DbgrpPrintProfilerStackData (
- PSTACK_DATA_ENTRY Root,
- ULONG Threshold
- );
- PMEMORY_POOL_ENTRY
- DbgrpGetMemoryPoolEntry (
- PLIST_ENTRY PoolListHead,
- PROFILER_MEMORY_TYPE ProfilerMemoryType
- );
- PPROFILER_MEMORY_POOL_TAG_STATISTIC
- DbgrpGetTagStatistics (
- PMEMORY_POOL_ENTRY MemoryPoolEntry,
- ULONG Tag
- );
- INT
- DbgrpDispatchStackProfilerCommand (
- PDEBUGGER_CONTEXT Context,
- PSTR *Arguments,
- ULONG ArgumentCount
- );
- INT
- DbgrpDispatchMemoryProfilerCommand (
- PDEBUGGER_CONTEXT Context,
- PSTR *Arguments,
- ULONG ArgumentCount
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a global containing the debugger context, assumed to be singular. This
- // is pretty awful and goes against the idea of the debugger context, but
- // many of these functions are called back from UI window procedures and timer
- // callbacks.
- //
- PDEBUGGER_CONTEXT DbgrProfilerGlobalContext;
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- DbgrProfilerInitialize (
- PDEBUGGER_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine initializes the debugger for profiler data consumption.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- 0 on success.
- Returns an error code on failure.
- --*/
- {
- INT Result;
- DbgrProfilerGlobalContext = Context;
- Context->ProfilingData.StackListLock = CreateDebuggerLock();
- if (Context->ProfilingData.StackListLock == NULL) {
- return ENOMEM;
- }
- Context->ProfilingData.MemoryListLock = CreateDebuggerLock();
- if (Context->ProfilingData.MemoryListLock == NULL) {
- return ENOMEM;
- }
- Result = DbgrpInitializeThreadProfiling(Context);
- if (Result != 0) {
- return Result;
- }
- INITIALIZE_LIST_HEAD(&(Context->ProfilingData.StackListHead));
- INITIALIZE_LIST_HEAD(&(Context->ProfilingData.MemoryListHead));
- Context->ProfilingData.MemoryCollectionActive = FALSE;
- return 0;
- }
- VOID
- DbgrProfilerDestroy (
- PDEBUGGER_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine destroys any structures used to consume profiler data.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- None.
- --*/
- {
- if (Context->ProfilingData.StackListLock != NULL) {
- AcquireDebuggerLock(Context->ProfilingData.StackListLock);
- DbgrpDestroyProfilerDataList(&(Context->ProfilingData.StackListHead));
- ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
- DestroyDebuggerLock(Context->ProfilingData.StackListLock);
- }
- if (Context->ProfilingData.MemoryListLock != NULL) {
- AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
- DbgrpDestroyProfilerDataList(&(Context->ProfilingData.MemoryListHead));
- ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
- DestroyDebuggerLock(Context->ProfilingData.MemoryListLock);
- }
- DbgrpDestroyThreadProfiling(Context);
- DbgrDestroyProfilerStackData(Context->ProfilingData.CommandLineStackRoot);
- DbgrDestroyProfilerMemoryData(
- Context->ProfilingData.CommandLinePoolListHead);
- DbgrDestroyProfilerMemoryData(
- Context->ProfilingData.CommandLineBaseListHead);
- return;
- }
- VOID
- DbgrProcessProfilerNotification (
- PDEBUGGER_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine processes a profiler notification that the debuggee sends to
- the debugger. The routine should collect the profiler data and return as
- quickly as possible.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- None.
- --*/
- {
- PPROFILER_DATA_ENTRY ProfilerData;
- PPROFILER_NOTIFICATION ProfilerNotification;
- BOOL Result;
- ProfilerData = NULL;
- //
- // Get the profiler notification data out of the current event.
- //
- ProfilerNotification = Context->CurrentEvent.ProfilerNotification;
- //
- // If the end packet was received, denoted by the max profiler type, then
- // close out this round of data collection.
- //
- if (ProfilerNotification->Header.Type >= ProfilerDataTypeMax) {
- AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
- //
- // Put the sentinel on the last entry if memory profiling is active.
- //
- if (Context->ProfilingData.MemoryCollectionActive != FALSE) {
- ASSERT(LIST_EMPTY(&(Context->ProfilingData.MemoryListHead)) ==
- FALSE);
- ProfilerData = LIST_VALUE(
- Context->ProfilingData.MemoryListHead.Previous,
- PROFILER_DATA_ENTRY,
- ListEntry);
- ProfilerData->Flags |= PROFILER_DATA_FLAGS_MEMORY_SENTINEL;
- Context->ProfilingData.MemoryCollectionActive = FALSE;
- }
- ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
- Result = TRUE;
- goto ProcessProfilerNotificationEnd;
- }
- //
- // This is a valid profiler data type. Create a profiler data list element
- // and copy this notification's data into the element.
- //
- ProfilerData = malloc(sizeof(PROFILER_DATA_ENTRY));
- if (ProfilerData == NULL) {
- Result = FALSE;
- goto ProcessProfilerNotificationEnd;
- }
- ProfilerData->Processor = ProfilerNotification->Header.Processor;
- ProfilerData->DataSize = ProfilerNotification->Header.DataSize;
- ProfilerData->Offset = 0;
- ProfilerData->Data = malloc(ProfilerData->DataSize);
- if (ProfilerData->Data == NULL) {
- Result = FALSE;
- goto ProcessProfilerNotificationEnd;
- }
- ProfilerData->Flags = 0;
- RtlCopyMemory(ProfilerData->Data,
- ProfilerNotification->Data,
- ProfilerData->DataSize);
- //
- // Insert the profile data into the correct list.
- //
- switch (ProfilerNotification->Header.Type) {
- case ProfilerDataTypeStack:
- //
- // Insert the element into the list of stack samples.
- //
- AcquireDebuggerLock(Context->ProfilingData.StackListLock);
- INSERT_BEFORE(&(ProfilerData->ListEntry),
- &(Context->ProfilingData.StackListHead));
- ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
- Result = TRUE;
- break;
- case ProfilerDataTypeMemory:
- //
- // Insert the element into the list of memory samples.
- //
- AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
- Context->ProfilingData.MemoryCollectionActive = TRUE;
- INSERT_BEFORE(&(ProfilerData->ListEntry),
- &(Context->ProfilingData.MemoryListHead));
- ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
- Result = TRUE;
- break;
- case ProfilerDataTypeThread:
- DbgrpProcessThreadProfilingData(Context, ProfilerData);
- Result = TRUE;
- break;
- default:
- DbgOut("Error: Unknown profiler notification type %d.\n",
- ProfilerNotification->Header.Type);
- Result = FALSE;
- break;
- }
- ProcessProfilerNotificationEnd:
- if (Result == FALSE) {
- if (ProfilerData != NULL) {
- free(ProfilerData);
- }
- }
- return;
- }
- INT
- DbgrDispatchProfilerCommand (
- PDEBUGGER_CONTEXT Context,
- PSTR *Arguments,
- ULONG ArgumentCount
- )
- /*++
- Routine Description:
- This routine handles a profiler command.
- Arguments:
- Context - Supplies a pointer to the application context.
- Arguments - Supplies an array of strings containing the arguments.
- ArgumentCount - Supplies the number of arguments in the Arguments array.
- Return Value:
- 0 on success.
- Returns an error code on failure.
- --*/
- {
- INT Result;
- //
- // Currently the profiler supports only one debug context.
- //
- assert(Context == DbgrProfilerGlobalContext);
- if (ArgumentCount < 1) {
- DbgOut(PROFILER_USAGE);
- return EINVAL;
- }
- if (strcasecmp(Arguments[0], "stack") == 0) {
- Result = DbgrpDispatchStackProfilerCommand(Context,
- Arguments,
- ArgumentCount);
- } else if (strcasecmp(Arguments[0], "memory") == 0) {
- Result = DbgrpDispatchMemoryProfilerCommand(Context,
- Arguments,
- ArgumentCount);
- } else if (strcasecmp(Arguments[0], "thread") == 0) {
- Result = DbgrpDispatchThreadProfilerCommand(Context,
- Arguments,
- ArgumentCount);
- } else if (strcasecmp(Arguments[0], "help") == 0) {
- DbgOut(PROFILER_USAGE);
- Result = 0;
- } else {
- DbgOut("Error: Invalid profiler type '%s'.\n\n", Arguments[0]);
- DbgOut(PROFILER_USAGE);
- Result = EINVAL;
- }
- return Result;
- }
- VOID
- DbgrDisplayCommandLineProfilerData (
- PROFILER_DATA_TYPE DataType,
- PROFILER_DISPLAY_REQUEST DisplayRequest,
- ULONG Threshold
- )
- /*++
- Routine Description:
- This routine displays the profiler data collected by the core debugging
- infrastructure to standard out.
- Arguments:
- DataType - Supplies the type of profiler data that is to be displayed.
- DisplayRequest - Supplies a value requesting a display action, which can
- either be to display data once, continually, or to stop continually
- displaying data.
- Threshold - Supplies the minimum percentage a stack entry hit must be in
- order to be displayed.
- Return Value:
- None.
- --*/
- {
- PDEBUGGER_CONTEXT Context;
- BOOL DeltaMode;
- PLIST_ENTRY PoolListHead;
- PDEBUGGER_PROFILING_DATA ProfilingData;
- BOOL Result;
- Context = DbgrProfilerGlobalContext;
- ProfilingData = &(Context->ProfilingData);
- switch (DisplayRequest) {
- case ProfilerDisplayOneTime:
- case ProfilerDisplayOneTimeThreshold:
- //
- // Display the profiler stack data once if there is any.
- //
- switch (DataType) {
- case ProfilerDataTypeStack:
- Result = DbgrGetProfilerStackData(
- &(ProfilingData->CommandLineStackRoot));
- if (Result == FALSE) {
- DbgOut("Error: There is no valid stack data to display.\n");
- return;
- }
- DbgrPrintProfilerStackData(ProfilingData->CommandLineStackRoot,
- Threshold);
- break;
- //
- // Display the profiler memory data if there is any.
- //
- case ProfilerDataTypeMemory:
- Result = DbgrGetProfilerMemoryData(&PoolListHead);
- if ((Result == FALSE) &&
- (ProfilingData->CommandLinePoolListHead == NULL)) {
- DbgOut("Error: There is no valid memory data to display.\n");
- return;
- }
- //
- // Always save the latest valid list in case there is no new data
- // for the next call.
- //
- if (Result != FALSE) {
- if (ProfilingData->CommandLinePoolListHead !=
- ProfilingData->CommandLineBaseListHead) {
- DbgrDestroyProfilerMemoryData(
- ProfilingData->CommandLinePoolListHead);
- }
- ProfilingData->CommandLinePoolListHead = PoolListHead;
- }
- //
- // Try to subtract the base line statistics and determine if delta
- // mode is enabled.
- //
- PoolListHead = DbgrSubtractMemoryStatistics(
- ProfilingData->CommandLinePoolListHead,
- ProfilingData->CommandLineBaseListHead);
- if (PoolListHead != ProfilingData->CommandLinePoolListHead) {
- DeltaMode = TRUE;
- } else {
- DeltaMode = FALSE;
- }
- //
- // Print the statistics to the console and destroy the temporary
- // list if a delta was displayed.
- //
- DbgrPrintProfilerMemoryData(PoolListHead, DeltaMode, Threshold);
- if (DeltaMode != FALSE) {
- DbgrDestroyProfilerMemoryData(PoolListHead);
- }
- break;
- default:
- DbgOut("Error: invalid profiler type %d.\n", DataType);
- break;
- }
- break;
- case ProfilerDisplayClear:
- switch (DataType) {
- case ProfilerDataTypeStack:
- DbgrDestroyProfilerStackData(ProfilingData->CommandLineStackRoot);
- Context->ProfilingData.CommandLineStackRoot = NULL;
- break;
- default:
- DbgOut("Error: invalid profiler type %d for the 'clear' command.\n",
- DataType);
- break;
- }
- break;
- case ProfilerDisplayStartDelta:
- switch (DataType) {
- //
- // Establish a base memory record to start delta mode.
- //
- case ProfilerDataTypeMemory:
- //
- // Use the most recent memory pool statistics if available.
- //
- DbgrDestroyProfilerMemoryData(
- ProfilingData->CommandLineBaseListHead);
- if (ProfilingData->CommandLinePoolListHead != NULL) {
- ProfilingData->CommandLineBaseListHead =
- ProfilingData->CommandLinePoolListHead;
- Result = TRUE;
- //
- // If they are not available, then query for new statistics.
- //
- } else {
- Result = DbgrGetProfilerMemoryData(&PoolListHead);
- if (Result == FALSE) {
- ProfilingData->CommandLineBaseListHead = NULL;
- DbgOut("There is no memory data available to establish a "
- "baseline for delta mode.\n");
- } else {
- ProfilingData->CommandLineBaseListHead = PoolListHead;
- ProfilingData->CommandLinePoolListHead = PoolListHead;
- }
- }
- if (Result != FALSE) {
- DbgOut("Memory profiler delta mode enabled.\n");
- }
- break;
- default:
- DbgOut("Error: invalid profiler type %d for the 'delta' command.\n",
- DataType);
- break;
- }
- break;
- case ProfilerDisplayStopDelta:
- switch (DataType) {
- //
- // Remove the record of a memory base to stop delta mode.
- //
- case ProfilerDataTypeMemory:
- if (ProfilingData->CommandLineBaseListHead !=
- ProfilingData->CommandLinePoolListHead) {
- DbgrDestroyProfilerMemoryData(
- ProfilingData->CommandLineBaseListHead);
- }
- ProfilingData->CommandLineBaseListHead = NULL;
- DbgOut("Memory profiler delta mode disabled.\n");
- break;
- default:
- DbgOut("Error: invalid profiler type %d for the 'delta' command.\n",
- DataType);
- break;
- }
- break;
- default:
- DbgOut("Error: Invalid profiler display request %d.\n", DisplayRequest);
- break;
- }
- return;
- }
- BOOL
- DbgrGetProfilerStackData (
- PSTACK_DATA_ENTRY *StackTreeRoot
- )
- /*++
- Routine Description:
- This routine processes and returns any pending profiling stack data. It
- will add it to the provided stack tree root. The caller is responsible for
- destroying the tree.
- Arguments:
- StackTreeRoot - Supplies a pointer to a pointer to the root of the stack
- data tree. Upon return from the routine it will be updated with all the
- newly parsed data. If the root is null, a new root will be allocated.
- Return Value:
- Returns TRUE when data is successfully returned, or FALSE on failure.
- --*/
- {
- ULONGLONG Address;
- PSTACK_DATA_ENTRY AllocatedRoot;
- PDEBUGGER_CONTEXT Context;
- PSTACK_DATA_ENTRY CurrentEntry;
- ULONG CurrentStackLength;
- PLIST_ENTRY DataEntry;
- ULONG Index;
- ULONGLONG Offset;
- PSTACK_DATA_ENTRY Parent;
- ULONG PointerSize;
- PPROFILER_DATA_ENTRY ProfilerData;
- BOOL Result;
- PSTACK_DATA_ENTRY Root;
- ULONG RoutineCount;
- PSTACK_DATA_ENTRY StackData;
- PLIST_ENTRY StackEntry;
- ULONG StackLength;
- LIST_ENTRY StackListHead;
- assert(StackTreeRoot != NULL);
- Context = DbgrProfilerGlobalContext;
- AllocatedRoot = NULL;
- Result = FALSE;
- INITIALIZE_LIST_HEAD(&StackListHead);
- //
- // If the tree root is NULL, create a new one for the caller.
- //
- if (*StackTreeRoot == NULL) {
- AllocatedRoot = DbgrpCreateStackEntry(DbgrProfilerGlobalContext,
- NULL,
- 0);
- if (AllocatedRoot == NULL) {
- Result = FALSE;
- goto GetProfilerStackDataEnd;
- }
- *StackTreeRoot = AllocatedRoot;
- }
- Root = *StackTreeRoot;
- //
- // Acquire the profiler lock and copy the head of the stack data list, fix
- // up the pointers, and empty the global list. If the list is currently
- // empty, just exit.
- //
- AcquireDebuggerLock(Context->ProfilingData.StackListLock);
- if (LIST_EMPTY(&(Context->ProfilingData.StackListHead)) != FALSE) {
- Result = TRUE;
- ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
- goto GetProfilerStackDataEnd;
- }
- RtlCopyMemory(&StackListHead,
- &(Context->ProfilingData.StackListHead),
- sizeof(LIST_ENTRY));
- StackListHead.Next->Previous = &StackListHead;
- StackListHead.Previous->Next = &StackListHead;
- INITIALIZE_LIST_HEAD(&(Context->ProfilingData.StackListHead));
- ReleaseDebuggerLock(Context->ProfilingData.StackListLock);
- //
- // Loop through each profiler stack data packet in the list, adding its
- // stack entries to the tree of stack data.
- //
- PointerSize = DbgGetTargetPointerSize(Context);
- DataEntry = StackListHead.Next;
- while (DataEntry != &StackListHead) {
- ProfilerData = LIST_VALUE(DataEntry, PROFILER_DATA_ENTRY, ListEntry);
- RoutineCount = ProfilerData->DataSize / PointerSize;
- if ((ProfilerData->DataSize % PointerSize) != 0) {
- DbgOut("Bad profiler data size %d.\n", ProfilerData->DataSize);
- Result = FALSE;
- goto GetProfilerStackDataEnd;
- }
- //
- // Run through the data array backwards to parse each stack from the
- // root routine.
- //
- Parent = Root;
- CurrentStackLength = 0;
- for (Index = RoutineCount; Index > 0; Index -= 1) {
- Address = 0;
- Offset = (Index - 1) * PointerSize;
- RtlCopyMemory(&Address, &(ProfilerData->Data[Offset]), PointerSize);
- //
- // Every sentinel encountered means that a call stack was
- // completely processed.
- //
- if (IS_PROFILER_DATA_SENTINEL(Address) != FALSE) {
- //
- // Validate that this was a complete stack. The stack size
- // stored in the sentinel marker includes the size of the
- // sentinel.
- //
- StackLength = GET_PROFILER_DATA_SIZE(Address) / PointerSize;
- if (CurrentStackLength != (StackLength - 1)) {
- DbgOut("Error: Profiler collected incomplete call "
- "stack.\n");
- Result = FALSE;
- goto GetProfilerStackDataEnd;
- }
- CurrentStackLength = 0;
- Root->Count += 1;
- Parent = Root;
- continue;
- }
- //
- // Look up the call site in the parent's list of children.
- //
- CurrentEntry = NULL;
- StackEntry = Parent->Children.Next;
- while (StackEntry != &(Parent->Children)) {
- StackData = LIST_VALUE(StackEntry,
- STACK_DATA_ENTRY,
- SiblingEntry);
- if (StackData->Address == Address) {
- CurrentEntry = StackData;
- break;
- }
- StackEntry = StackEntry->Next;
- }
- //
- // If there was no match, create a new entry. If this fails,
- // just exit returning failure.
- //
- if (CurrentEntry == NULL) {
- CurrentEntry = DbgrpCreateStackEntry(DbgrProfilerGlobalContext,
- Parent,
- Address);
- if (CurrentEntry == NULL) {
- DbgOut("Error: Failed to create stack entry.\n");
- Result = FALSE;
- goto GetProfilerStackDataEnd;
- }
- }
- //
- // Account for this match on the current entry, remove it, and
- // then insert it back into the stack in order.
- //
- CurrentEntry->Count += 1;
- LIST_REMOVE(&(CurrentEntry->SiblingEntry));
- DbgrpInsertStackData(Parent, CurrentEntry);
- //
- // Move down the stack.
- //
- Parent = CurrentEntry;
- CurrentStackLength += 1;
- }
- //
- // Move on to the next entry.
- //
- DataEntry = DataEntry->Next;
- }
- Result = TRUE;
- GetProfilerStackDataEnd:
- DbgrpDestroyProfilerDataList(&StackListHead);
- //
- // If the routine failed and allocated the root, destroy the tree.
- //
- if (Result == FALSE) {
- if (AllocatedRoot != NULL) {
- DbgrDestroyProfilerStackData(AllocatedRoot);
- if (*StackTreeRoot == AllocatedRoot) {
- *StackTreeRoot = NULL;
- }
- }
- }
- return Result;
- }
- VOID
- DbgrDestroyProfilerStackData (
- PSTACK_DATA_ENTRY Root
- )
- /*++
- Routine Description:
- This routine destroys a profiler stack data tree.
- Arguments:
- Root - Supplies a pointer to the root element of the tree.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PSTACK_DATA_ENTRY StackData;
- if (Root == NULL) {
- return;
- }
- //
- // Recursively destroy all the children.
- //
- while (LIST_EMPTY(&(Root->Children)) == FALSE) {
- CurrentEntry = Root->Children.Next;
- StackData = LIST_VALUE(CurrentEntry, STACK_DATA_ENTRY, SiblingEntry);
- DbgrDestroyProfilerStackData(StackData);
- }
- //
- // Now destroy the current root.
- //
- if (Root->SiblingEntry.Next != NULL) {
- LIST_REMOVE(&(Root->SiblingEntry));
- }
- if (Root->AddressSymbol != NULL) {
- free(Root->AddressSymbol);
- }
- free(Root);
- return;
- }
- VOID
- DbgrPrintProfilerStackData (
- PSTACK_DATA_ENTRY Root,
- ULONG Threshold
- )
- /*++
- Routine Description:
- This routine prints profiler stack data to standard out.
- Arguments:
- Root - Supplies a pointer to the root of the profiler stack data tree.
- Threshold - Supplies the minimum percentage a stack entry hit must be in
- order to be displayed.
- Return Value:
- None.
- --*/
- {
- DbgrpPrintProfilerStackData(Root, Threshold);
- return;
- }
- VOID
- DbgrProfilerStackEntrySelected (
- PSTACK_DATA_ENTRY Root
- )
- /*++
- Routine Description:
- This routine is called when a profiler stack data entry is selected by the
- user.
- Arguments:
- Root - Supplies a pointer to the root of the profiler stack data tree.
- Return Value:
- None.
- --*/
- {
- //
- // If an entry is found, highlight the code line associated with the
- // selected item. This operation will remove the highlight from the
- // previously selected item.
- //
- if ((Root != NULL) && (Root->Address != 0)) {
- DbgrShowSourceAtAddress(DbgrProfilerGlobalContext, Root->Address);
- }
- return;
- }
- BOOL
- DbgrGetProfilerMemoryData (
- PLIST_ENTRY *MemoryPoolListHead
- )
- /*++
- Routine Description:
- This routine processes and returns any pending profiling memory data.
- Arguments:
- MemoryPoolListHead - Supplies a pointer to head of the memory pool list
- that is to be populated with the most up to date pool data.
- Return Value:
- Returns TRUE when data is successfully returned, or FALSE on failure.
- --*/
- {
- ULONG BytesRemaining;
- PDEBUGGER_CONTEXT Context;
- BYTE *Data;
- ULONG DataSize;
- LIST_ENTRY LocalListHead;
- PLIST_ENTRY MemoryListEntry;
- LIST_ENTRY MemoryListHead;
- PMEMORY_POOL_ENTRY MemoryPoolEntry;
- PLIST_ENTRY NewPoolListHead;
- ULONG Offset;
- PPROFILER_DATA_ENTRY ProfilerData;
- BOOL Result;
- ULONG SentinelCount;
- ULONG TagCount;
- ULONG TagSize;
- Context = DbgrProfilerGlobalContext;
- Data = NULL;
- NewPoolListHead = NULL;
- INITIALIZE_LIST_HEAD(&LocalListHead);
- INITIALIZE_LIST_HEAD(&MemoryListHead);
- //
- // Allocate a new pool list head to return to the caller if successful.
- //
- NewPoolListHead = malloc(sizeof(LIST_ENTRY));
- if (NewPoolListHead == NULL) {
- Result = FALSE;
- goto GetProfilerMemoryDataEnd;
- }
- INITIALIZE_LIST_HEAD(NewPoolListHead);
- //
- // Acquire the profiler memory lock and remove all complete memory data
- // packets.
- //
- AcquireDebuggerLock(Context->ProfilingData.MemoryListLock);
- //
- // Do nothing if the list is empty.
- //
- if (LIST_EMPTY(&(Context->ProfilingData.MemoryListHead)) != FALSE) {
- Result = FALSE;
- ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
- goto GetProfilerMemoryDataEnd;
- }
- //
- // First remove all the data packets.
- //
- RtlCopyMemory(&LocalListHead,
- &(Context->ProfilingData.MemoryListHead),
- sizeof(LIST_ENTRY));
- LocalListHead.Next->Previous = &LocalListHead;
- LocalListHead.Previous->Next = &LocalListHead;
- INITIALIZE_LIST_HEAD(&(Context->ProfilingData.MemoryListHead));
- //
- // Now run backwards through the local list, copying packets back to the
- // global list until the first sentinel is encountered.
- //
- while (LIST_EMPTY(&LocalListHead) == FALSE) {
- MemoryListEntry = LocalListHead.Previous;
- ProfilerData = LIST_VALUE(MemoryListEntry,
- PROFILER_DATA_ENTRY,
- ListEntry);
- if ((ProfilerData->Flags & PROFILER_DATA_FLAGS_MEMORY_SENTINEL) != 0) {
- break;
- }
- LIST_REMOVE(MemoryListEntry);
- INSERT_AFTER(MemoryListEntry, &(Context->ProfilingData.MemoryListHead));
- }
- ReleaseDebuggerLock(Context->ProfilingData.MemoryListLock);
- Result = TRUE;
- //
- // If this list is empty, just leave.
- //
- if (LIST_EMPTY(&LocalListHead) != FALSE) {
- Result = FALSE;
- goto GetProfilerMemoryDataEnd;
- }
- //
- // Only the most recent memory data is interesting, so out of the list of
- // completed memory snapshots, find the start of the last one.
- //
- SentinelCount = 0;
- INITIALIZE_LIST_HEAD(&MemoryListHead);
- while (LIST_EMPTY(&LocalListHead) == FALSE) {
- MemoryListEntry = LocalListHead.Previous;
- ProfilerData = LIST_VALUE(MemoryListEntry,
- PROFILER_DATA_ENTRY,
- ListEntry);
- if ((ProfilerData->Flags & PROFILER_DATA_FLAGS_MEMORY_SENTINEL) != 0) {
- SentinelCount += 1;
- if (SentinelCount > 1) {
- break;
- }
- }
- LIST_REMOVE(MemoryListEntry);
- INSERT_AFTER(MemoryListEntry, &MemoryListHead);
- }
- //
- // Release the outdated information.
- //
- DbgrpDestroyProfilerDataList(&LocalListHead);
- //
- // Now package the data into what the debugger UI consoles expect. Start
- // by pulling all the data into one buffer, it may have been awkwardly
- // split across packets.
- //
- DataSize = 0;
- MemoryListEntry = MemoryListHead.Next;
- while (MemoryListEntry != &MemoryListHead) {
- ProfilerData = LIST_VALUE(MemoryListEntry,
- PROFILER_DATA_ENTRY,
- ListEntry);
- DataSize += ProfilerData->DataSize;
- MemoryListEntry = MemoryListEntry->Next;
- }
- Data = malloc(DataSize);
- if (Data == NULL) {
- DbgOut("Error: failed to allocate %d bytes for the memory profiler.\n",
- DataSize);
- Result = FALSE;
- goto GetProfilerMemoryDataEnd;
- }
- Offset = 0;
- MemoryListEntry = MemoryListHead.Next;
- while (MemoryListEntry != &MemoryListHead) {
- ProfilerData = LIST_VALUE(MemoryListEntry,
- PROFILER_DATA_ENTRY,
- ListEntry);
- RtlCopyMemory(&(Data[Offset]),
- ProfilerData->Data,
- ProfilerData->DataSize);
- Offset += ProfilerData->DataSize;
- MemoryListEntry = MemoryListEntry->Next;
- }
- //
- // With all the data copied, destroy the list.
- //
- DbgrpDestroyProfilerDataList(&MemoryListHead);
- //
- // Now read through the data buffer, translating the byte segments into the
- // appropriate structures.
- //
- Offset = 0;
- BytesRemaining = DataSize;
- while (BytesRemaining != 0) {
- if (BytesRemaining < sizeof(PROFILER_MEMORY_POOL)) {
- DbgOut("Error: invalid memory pool data.\n");
- Result = FALSE;
- goto GetProfilerMemoryDataEnd;
- }
- //
- // Allocate a pool entry in anticipation of a valid data buffer.
- //
- MemoryPoolEntry = malloc(sizeof(MEMORY_POOL_ENTRY));
- if (MemoryPoolEntry == NULL) {
- DbgOut("Error: failed to allocate %d bytes for a memory pool "
- "entry.\n",
- sizeof(MEMORY_POOL_ENTRY));
- Result = FALSE;
- goto GetProfilerMemoryDataEnd;
- }
- //
- // Copy the memory into the pool entry.
- //
- RtlCopyMemory(&(MemoryPoolEntry->MemoryPool),
- &(Data[Offset]),
- sizeof(PROFILER_MEMORY_POOL));
- Offset += sizeof(PROFILER_MEMORY_POOL);
- BytesRemaining -= sizeof(PROFILER_MEMORY_POOL);
- //
- // If this is not a pool header, then exit.
- //
- if (MemoryPoolEntry->MemoryPool.Magic != PROFILER_POOL_MAGIC) {
- DbgOut("Error: found 0x%08x when expected pool magic 0x%08x.\n",
- MemoryPoolEntry->MemoryPool.Magic,
- PROFILER_POOL_MAGIC);
- Result = FALSE;
- free(MemoryPoolEntry);
- goto GetProfilerMemoryDataEnd;
- }
- //
- // Determine the number of tag statistics in this pool and whether or
- // not the data buffer is big enough to hold the expected tag data. If
- // not, then exit.
- //
- TagCount = MemoryPoolEntry->MemoryPool.TagCount;
- TagSize = TagCount * sizeof(PROFILER_MEMORY_POOL_TAG_STATISTIC);
- if (BytesRemaining < TagSize) {
- DbgOut("Error: unexpected end of memory data buffer. %d bytes "
- "remaining when expected %d bytes.\n",
- BytesRemaining,
- TagSize);
- Result = FALSE;
- free(MemoryPoolEntry);
- goto GetProfilerMemoryDataEnd;
- }
- //
- // Allocate an array for the tag statistics.
- //
- MemoryPoolEntry->TagStatistics = malloc(TagSize);
- if (MemoryPoolEntry->TagStatistics == NULL) {
- DbgOut("Error: failed to allocate %d bytes for a memory pool "
- "tag statistics.\n",
- TagSize);
- Result = FALSE;
- free(MemoryPoolEntry);
- goto GetProfilerMemoryDataEnd;
- }
- //
- // Copy the tag statistics.
- //
- RtlCopyMemory(MemoryPoolEntry->TagStatistics, &(Data[Offset]), TagSize);
- Offset += TagSize;
- BytesRemaining -= TagSize;
- //
- // Insert this complete pool data into the supplied list head.
- //
- INSERT_BEFORE(&(MemoryPoolEntry->ListEntry), NewPoolListHead);
- }
- *MemoryPoolListHead = NewPoolListHead;
- Result = TRUE;
- GetProfilerMemoryDataEnd:
- if (Result == FALSE) {
- DbgrpDestroyProfilerDataList(&MemoryListHead);
- DbgrpDestroyProfilerDataList(&LocalListHead);
- if (Data != NULL) {
- free(Data);
- }
- if (NewPoolListHead != NULL) {
- DbgrDestroyProfilerMemoryData(NewPoolListHead);
- }
- }
- return Result;
- }
- VOID
- DbgrDestroyProfilerMemoryData (
- PLIST_ENTRY PoolListHead
- )
- /*++
- Routine Description:
- This routine destroys a profiler memory data list.
- Arguments:
- PoolListHead - Supplies a pointer to the head of the memory pool list
- that is to be destroyed.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PMEMORY_POOL_ENTRY MemoryPoolEntry;
- if (PoolListHead == NULL) {
- return;
- }
- //
- // Destroy each element in the list.
- //
- while (LIST_EMPTY(PoolListHead) == FALSE) {
- CurrentEntry = PoolListHead->Next;
- MemoryPoolEntry = LIST_VALUE(CurrentEntry,
- MEMORY_POOL_ENTRY,
- ListEntry);
- LIST_REMOVE(CurrentEntry);
- if (MemoryPoolEntry->TagStatistics != NULL) {
- free(MemoryPoolEntry->TagStatistics);
- }
- free(MemoryPoolEntry);
- }
- free(PoolListHead);
- return;
- }
- VOID
- DbgrPrintProfilerMemoryData (
- PLIST_ENTRY MemoryPoolListHead,
- BOOL DeltaMode,
- ULONG ActiveCountThreshold
- )
- /*++
- Routine Description:
- This routine prints the statistics from the given memory pool list to the
- debugger console.
- Arguments:
- MemoryPoolListHead - Supplies a pointer to the head of the memory pool
- list.
- DeltaMode - Supplies a boolean indicating whether or not the memory pool
- list represents a delta from a previous point in time.
- ActiveCountThreshold - Supplies the active count threshold. No statistics
- will be displayed for tags with an active count less than this
- threshold.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- LONG DeltaAllocationCount;
- LONG DeltaThreshold;
- ULONG FreePercentage;
- ULONG Index;
- PPROFILER_MEMORY_POOL Pool;
- PMEMORY_POOL_ENTRY PoolEntry;
- PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
- CurrentEntry = MemoryPoolListHead->Next;
- while (CurrentEntry != MemoryPoolListHead) {
- PoolEntry = LIST_VALUE(CurrentEntry, MEMORY_POOL_ENTRY, ListEntry);
- Pool = &(PoolEntry->MemoryPool);
- //
- // Print the pool statistics.
- //
- if (Pool->TotalPoolSize != 0) {
- FreePercentage = Pool->FreeListSize * 100 / Pool->TotalPoolSize;
- DbgOut("Pool Type %d, Size %I64xh, %d%% free, "
- "%I64d allocation calls, %I64d free calls, %I64d failed.\n",
- Pool->ProfilerMemoryType,
- Pool->TotalPoolSize,
- FreePercentage,
- Pool->TotalAllocationCalls,
- Pool->TotalFreeCalls,
- Pool->FailedAllocations);
- } else {
- ASSERT(Pool->FreeListSize == 0);
- ASSERT(DeltaMode != FALSE);
- DbgOut("Pool Type %d, Size -, -%% free, "
- "%I64d allocation calls, %I64d free calls, %I64d failed.\n",
- Pool->ProfilerMemoryType,
- Pool->TotalAllocationCalls,
- Pool->TotalFreeCalls,
- Pool->FailedAllocations);
- }
- DbgOut("------------------------------------------------------------"
- "----------------------------\n"
- " Largest Active "
- "Max Active\n"
- "Tag Alloc Active Bytes Max Active Bytes Count "
- " Count Lifetime Alloc\n"
- "------------------------------------------------------------"
- "----------------------------\n");
- //
- // Loop through the tags in the pool, printing statistics for each.
- //
- for (Index = 0; Index < Pool->TagCount; Index += 1) {
- Statistic = &(PoolEntry->TagStatistics[Index]);
- if (DeltaMode == FALSE) {
- //
- // Skip statistics that are below the active count threshold.
- //
- if (Statistic->ActiveAllocationCount < ActiveCountThreshold) {
- continue;
- }
- DbgOut("%c%c%c%c %8xh %16I64xh %16I64xh %8d %8d %16I64xh\n",
- (UCHAR)(Statistic->Tag),
- (UCHAR)(Statistic->Tag >> 8),
- (UCHAR)(Statistic->Tag >> 16),
- (UCHAR)(Statistic->Tag >> 24),
- Statistic->LargestAllocation,
- Statistic->ActiveSize,
- Statistic->LargestActiveSize,
- Statistic->ActiveAllocationCount,
- Statistic->LargestActiveAllocationCount,
- Statistic->LifetimeAllocationSize);
- } else {
- //
- // Honor the threshold, the absolute value of both values must
- // be obtained in delta mode.
- //
- DeltaAllocationCount = (LONG)Statistic->ActiveAllocationCount;
- DeltaThreshold = (LONG)ActiveCountThreshold;
- if (DeltaAllocationCount < 0) {
- DeltaAllocationCount = -DeltaAllocationCount;
- }
- if (DeltaThreshold < 0) {
- DeltaThreshold = -DeltaThreshold;
- }
- if (DeltaAllocationCount < DeltaThreshold) {
- continue;
- }
- //
- // Only print on tags in delta mode if there is data present.
- //
- if ((Statistic->ActiveSize == 0) &&
- (Statistic->ActiveAllocationCount == 0) &&
- (Statistic->LifetimeAllocationSize == 0) &&
- (Statistic->LargestAllocation == 0) &&
- (Statistic->LargestActiveAllocationCount == 0) &&
- (Statistic->LargestActiveSize == 0)) {
- continue;
- }
- DbgOut("%c%c%c%c ",
- (UCHAR)(Statistic->Tag),
- (UCHAR)(Statistic->Tag >> 8),
- (UCHAR)(Statistic->Tag >> 16),
- (UCHAR)(Statistic->Tag >> 24));
- if (Statistic->LargestAllocation != 0) {
- DbgOut("%8xh ", Statistic->LargestAllocation);
- } else {
- DbgOut(" - ");
- }
- if (Statistic->ActiveSize != 0) {
- DbgOut(" %16I64d ", Statistic->ActiveSize);
- } else {
- DbgOut(" - ");
- }
- if (Statistic->LargestActiveSize != 0) {
- DbgOut("%16I64xh ", Statistic->LargestActiveSize);
- } else {
- DbgOut(" - ");
- }
- if (Statistic->ActiveAllocationCount != 0) {
- DbgOut("%8d ", Statistic->ActiveAllocationCount);
- } else {
- DbgOut(" - ");
- }
- if (Statistic->LargestActiveAllocationCount != 0) {
- DbgOut("%8d ", Statistic->LargestActiveAllocationCount);
- } else {
- DbgOut(" - ");
- }
- if (Statistic->LifetimeAllocationSize != 0) {
- DbgOut("%16I64xh\n", Statistic->LifetimeAllocationSize);
- } else {
- DbgOut(" -\n");
- }
- }
- }
- DbgOut("\n");
- CurrentEntry = CurrentEntry->Next;
- }
- return;
- }
- PLIST_ENTRY
- DbgrSubtractMemoryStatistics (
- PLIST_ENTRY CurrentListHead,
- PLIST_ENTRY BaseListHead
- )
- /*++
- Routine Description:
- This routine subtracts the given current memory list from the base memory
- list, returning a list that contains the deltas for memory pool statistics.
- If this routine ever fails, it just returns the current list.
- Arguments:
- CurrentListHead - Supplies a pointer to the head of the current list of
- memory pool data.
- BaseListHead - Supplies a pointer to the head of the base line memory list
- from which the deltas are created.
- Return Value:
- Returns a new memory pool list if possible. If the routine fails or there
- is no base line, then the current memory list is returned.
- --*/
- {
- PPROFILER_MEMORY_POOL BaseMemoryPool;
- PMEMORY_POOL_ENTRY BaseMemoryPoolEntry;
- PPROFILER_MEMORY_POOL_TAG_STATISTIC BaseStatistic;
- PLIST_ENTRY CurrentEntry;
- BOOL DestroyNewList;
- ULONG Index;
- PPROFILER_MEMORY_POOL MemoryPool;
- PMEMORY_POOL_ENTRY MemoryPoolEntry;
- PLIST_ENTRY NewListHead;
- PPROFILER_MEMORY_POOL NewMemoryPool;
- PMEMORY_POOL_ENTRY NewMemoryPoolEntry;
- PROFILER_MEMORY_TYPE ProfilerMemoryType;
- PLIST_ENTRY ResultListHead;
- PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
- ULONG TagCount;
- ULONG TagSize;
- assert(CurrentListHead != NULL);
- DestroyNewList = FALSE;
- NewListHead = NULL;
- //
- // Always return the current list head unless the subtraction succeeds.
- //
- ResultListHead = CurrentListHead;
- //
- // Do nothing if the base line statistics are NULL.
- //
- if (BaseListHead == NULL) {
- goto SubtractMemoryStatisticsEnd;
- }
- //
- // Otherwise, create a new list to return the subtracted list.
- //
- NewListHead = malloc(sizeof(LIST_ENTRY));
- if (NewListHead == NULL) {
- goto SubtractMemoryStatisticsEnd;
- }
- INITIALIZE_LIST_HEAD(NewListHead);
- //
- // Loop through the current memory pool list, subtracting the value from
- // the baseline.
- //
- CurrentEntry = CurrentListHead->Next;
- while (CurrentEntry != CurrentListHead) {
- //
- // Get the memory pool entry in the current list.
- //
- MemoryPoolEntry = LIST_VALUE(CurrentEntry,
- MEMORY_POOL_ENTRY,
- ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // Create a new pool entry and a new memory pool.
- //
- NewMemoryPoolEntry = malloc(sizeof(MEMORY_POOL_ENTRY));
- if (NewMemoryPoolEntry == NULL) {
- DestroyNewList = TRUE;
- goto SubtractMemoryStatisticsEnd;
- }
- //
- // Allocate the array for tag statistics.
- //
- MemoryPool = &(MemoryPoolEntry->MemoryPool);
- TagCount = MemoryPool->TagCount;
- TagSize = TagCount * sizeof(PROFILER_MEMORY_POOL_TAG_STATISTIC);
- NewMemoryPoolEntry->TagStatistics = malloc(TagSize);
- if (NewMemoryPoolEntry->TagStatistics == NULL) {
- free(NewMemoryPoolEntry);
- DestroyNewList = TRUE;
- goto SubtractMemoryStatisticsEnd;
- }
- //
- // Copy the the current pool contents.
- //
- NewMemoryPool = &(NewMemoryPoolEntry->MemoryPool);
- RtlCopyMemory(NewMemoryPool, MemoryPool, sizeof(PROFILER_MEMORY_POOL));
- //
- // And the tag statistics.
- //
- RtlCopyMemory(NewMemoryPoolEntry->TagStatistics,
- MemoryPoolEntry->TagStatistics,
- TagSize);
- //
- // Add the new pool entry to the new list.
- //
- INSERT_BEFORE(&(NewMemoryPoolEntry->ListEntry), NewListHead);
- //
- // Find the corresponding memory pool entry in the base list. If it
- // does not exist, then this pool is brand new, don't do any
- // subtraction.
- //
- ProfilerMemoryType = MemoryPool->ProfilerMemoryType;
- BaseMemoryPoolEntry = DbgrpGetMemoryPoolEntry(BaseListHead,
- ProfilerMemoryType);
- if (BaseMemoryPoolEntry == NULL) {
- continue;
- }
- //
- // Now subtract the base statistics from the new copy of the current
- // statistics.
- //
- BaseMemoryPool = &(BaseMemoryPoolEntry->MemoryPool);
- if ((NewMemoryPool->TotalPoolSize == BaseMemoryPool->TotalPoolSize) &&
- (NewMemoryPool->FreeListSize == BaseMemoryPool->FreeListSize)) {
- NewMemoryPool->TotalPoolSize = 0;
- NewMemoryPool->FreeListSize = 0;
- }
- NewMemoryPool->FailedAllocations -= BaseMemoryPool->FailedAllocations;
- NewMemoryPool->TotalFreeCalls -= BaseMemoryPool->TotalFreeCalls;
- NewMemoryPool->TotalAllocationCalls -=
- BaseMemoryPool->TotalAllocationCalls;
- //
- // Loop through the tag statistics and subtract the base statsitics.
- //
- for (Index = 0; Index < TagCount; Index += 1) {
- Statistic = &(NewMemoryPoolEntry->TagStatistics[Index]);
- //
- // Find the corresponding memory pool entry in the base list.
- //
- BaseStatistic = DbgrpGetTagStatistics(BaseMemoryPoolEntry,
- Statistic->Tag);
- if (BaseStatistic == NULL) {
- continue;
- }
- //
- // Subtract the base statistics from the current statistics.
- //
- if (Statistic->LargestAllocation ==
- BaseStatistic->LargestAllocation) {
- Statistic->LargestAllocation = 0;
- }
- if (Statistic->LargestActiveSize ==
- BaseStatistic->LargestActiveSize) {
- Statistic->LargestActiveSize = 0;
- }
- if (Statistic->LifetimeAllocationSize ==
- BaseStatistic->LifetimeAllocationSize) {
- Statistic->LifetimeAllocationSize = 0;
- }
- if (Statistic->LargestActiveAllocationCount ==
- BaseStatistic->LargestActiveAllocationCount) {
- Statistic->LargestActiveAllocationCount = 0;
- }
- Statistic->ActiveSize -= BaseStatistic->ActiveSize;
- Statistic->ActiveAllocationCount -=
- BaseStatistic->ActiveAllocationCount;
- }
- }
- ResultListHead = NewListHead;
- SubtractMemoryStatisticsEnd:
- if (DestroyNewList != FALSE) {
- DbgrDestroyProfilerMemoryData(NewListHead);
- }
- return ResultListHead;
- }
- VOID
- DbgrpDestroyProfilerDataList (
- PLIST_ENTRY ListHead
- )
- /*++
- Routine Description:
- This routine destroys a profiler data list. It does not destroy the head
- of the list.
- Arguments:
- ListHead - Supplies a pointer to the head of the list that is to be
- destroyed.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY DataEntry;
- PPROFILER_DATA_ENTRY ProfilerData;
- assert(ListHead != NULL);
- while (LIST_EMPTY(ListHead) == FALSE) {
- DataEntry = ListHead->Next;
- ProfilerData = LIST_VALUE(DataEntry, PROFILER_DATA_ENTRY, ListEntry);
- LIST_REMOVE(DataEntry);
- free(ProfilerData);
- }
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- PSTACK_DATA_ENTRY
- DbgrpCreateStackEntry (
- PDEBUGGER_CONTEXT Context,
- PSTACK_DATA_ENTRY Parent,
- ULONGLONG Address
- )
- /*++
- Routine Description:
- This routine creates a stack entry and inserts it into the parent's list of
- children.
- Arguments:
- Context - Supplies a pointer to the application context.
- Parent - Supplies a pointer to the stack entry's parent.
- Address - Supplies the call site address for this stack entry.
- ReturnAddress - Supplies the return address for this stack entry.
- Return Value:
- Returns a pointer to a stack entry on success, or NULL on failure.
- --*/
- {
- PSTR AddressSymbol;
- BOOL Result;
- PSTACK_DATA_ENTRY StackData;
- AddressSymbol = NULL;
- Result = FALSE;
- //
- // Allocate a new stack data entry and begin filling it in.
- //
- StackData = malloc(sizeof(STACK_DATA_ENTRY));
- if (StackData == NULL) {
- DbgOut("Error: Failed to allocate %d bytes.\n",
- sizeof(STACK_DATA_ENTRY));
- goto CreateStackDataEntryEnd;
- }
- RtlZeroMemory(StackData, sizeof(STACK_DATA_ENTRY));
- INITIALIZE_LIST_HEAD(&(StackData->Children));
- INITIALIZE_LIST_HEAD(&(StackData->SiblingEntry));
- StackData->Address = Address;
- StackData->Parent = Parent;
- assert(StackData->Count == 0);
- assert(StackData->UiHandle == NULL);
- //
- // If the parent is NULL, then this is the root. Just exit.
- //
- if (Parent == NULL) {
- assert(StackData->AddressSymbol == NULL);
- Result = TRUE;
- goto CreateStackDataEntryEnd;
- }
- //
- // Get the name for the stack data entry.
- //
- AddressSymbol = DbgGetAddressSymbol(Context, Address, NULL);
- if (AddressSymbol == NULL) {
- DbgOut("Error: failed to get symbol for address 0x%I64x.\n", Address);
- goto CreateStackDataEntryEnd;
- }
- StackData->AddressSymbol = AddressSymbol;
- //
- // Insert this new stack entry into the parent's list of children in order.
- //
- if (Parent != NULL) {
- DbgrpInsertStackData(Parent, StackData);
- }
- Result = TRUE;
- CreateStackDataEntryEnd:
- if (Result == FALSE) {
- if (StackData != NULL) {
- free(StackData);
- StackData = NULL;
- }
- if (AddressSymbol != NULL) {
- free(AddressSymbol);
- }
- }
- return StackData;
- }
- VOID
- DbgrpInsertStackData (
- PSTACK_DATA_ENTRY Parent,
- PSTACK_DATA_ENTRY Child
- )
- /*++
- Routine Description:
- This routine inserts the child into the parent's list of children in the
- correct order.
- Arguments:
- Parent - Supplies a pointer to the parent stack entry.
- Child - Supplies a pointer to the stack entry that is to be inserted.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PSTACK_DATA_ENTRY StackData;
- //
- // The list of children is already sorted, so just search for the correct
- // location to insert it. Going backwards makes this easier.
- //
- CurrentEntry = Parent->Children.Previous;
- while (CurrentEntry != &(Parent->Children)) {
- StackData = LIST_VALUE(CurrentEntry, STACK_DATA_ENTRY, SiblingEntry);
- if (Child->Count < StackData->Count) {
- INSERT_AFTER(&(Child->SiblingEntry), &(StackData->SiblingEntry));
- return;
- }
- if ((Child->Count == StackData->Count) &&
- (Child->Address > StackData->Address)) {
- INSERT_AFTER(&(Child->SiblingEntry), &(StackData->SiblingEntry));
- return;
- }
- CurrentEntry = CurrentEntry->Previous;
- }
- //
- // It was not inserted, just place it at the beginning.
- //
- INSERT_AFTER(&(Child->SiblingEntry), &(Parent->Children));
- return;
- }
- VOID
- DbgrpPrintProfilerStackData (
- PSTACK_DATA_ENTRY Root,
- ULONG Threshold
- )
- /*++
- Routine Description:
- This routine prints information for the given profiler stack data entry and
- all its children to standard out.
- Arguments:
- Root - Supplies a pointer to the root of the stack data tree.
- Threshold - Supplies the minimum percentage a stack entry hit must be in
- order to be displayed.
- Return Value:
- None.
- --*/
- {
- BOOL AddVerticalBar;
- PSTACK_DATA_ENTRY ChildData;
- PSTR FunctionString;
- PSTR IndentString;
- ULONG IndentStringLength;
- PSTR NewIndentString;
- PSTACK_DATA_ENTRY NextEntry;
- ULONG NextPercent;
- ULONG Percent;
- PSTACK_DATA_ENTRY StackData;
- ULONG TotalCount;
- TotalCount = Root->Count;
- //
- // Return if the total count is zero. There is nothing to do.
- //
- if (TotalCount == 0) {
- return;
- }
- //
- // The nodes need to be displayed in depth first order where a parent is
- // displayed before any of its children. So the iteration order is to
- // process the current node, then move to either its first child, its next
- // sibling, or an anscestor's sibling. Of course, only move to nodes if
- // they meet the threshold requirements.
- //
- IndentString = malloc(sizeof(UCHAR));
- IndentStringLength = sizeof(UCHAR);
- *IndentString = '\0';
- StackData = Root;
- do {
- Percent = (StackData->Count * 100) / TotalCount;
- assert(Percent >= Threshold);
- //
- // Display the indent string.
- //
- DbgOut(IndentString);
- //
- // If this element has children, print the appropriate start symbol
- // depending on whether or not the element has children. Don't worry
- // about whether or not the children meet the threshold; either way
- // this serves as an indication that there are children.
- //
- if (LIST_EMPTY(&(StackData->Children)) == FALSE) {
- DbgOut(" +");
- } else {
- DbgOut(" -");
- }
- //
- // Print the stack entry's information.
- //
- if (StackData->AddressSymbol == NULL) {
- FunctionString = "Root";
- } else {
- FunctionString = StackData->AddressSymbol;
- }
- DbgOut("%s: %d%%, %d\n", FunctionString, Percent, StackData->Count);
- //
- // Move to the first child if it meets the threshold. The children are
- // always sorted by hit count, so only the first child needs to be
- // checked.
- //
- if (LIST_EMPTY(&(StackData->Children)) == FALSE) {
- ChildData = LIST_VALUE(StackData->Children.Next,
- STACK_DATA_ENTRY,
- SiblingEntry);
- Percent = (ChildData->Count * 100) / TotalCount;
- if (Percent >= Threshold) {
- //
- // Determine the length of the child's indent string.
- //
- IndentStringLength += PROFILER_STACK_INDENT_LENGTH;
- NewIndentString = realloc(IndentString, IndentStringLength);
- if (NewIndentString == NULL) {
- DbgOut("Allocation failure\n");
- free(IndentString);
- return;
- }
- IndentString = NewIndentString;
- AddVerticalBar = FALSE;
- //
- // If the current node's next sibling will meet the threshold,
- // then a vertical bar needs to be added to the indent.
- //
- if ((StackData->Parent != NULL) &&
- (StackData->SiblingEntry.Next !=
- &(StackData->Parent->Children))) {
- NextEntry = LIST_VALUE(StackData->SiblingEntry.Next,
- STACK_DATA_ENTRY,
- SiblingEntry);
- NextPercent = (NextEntry->Count * 100) / TotalCount;
- if (NextPercent >= Threshold) {
- AddVerticalBar = TRUE;
- }
- }
- IndentString[IndentStringLength - 3] = ' ';
- if (AddVerticalBar == FALSE) {
- IndentString[IndentStringLength - 2] = ' ';
- } else {
- IndentString[IndentStringLength - 2] = '|';
- }
- IndentString[IndentStringLength - 1] = '\0';
- StackData = ChildData;
- continue;
- }
- }
- //
- // The first child didn't exists or didn't meet the threshold. Search
- // until a sibling or ancestor sibling needs to be processed. As the
- // children are in order, only one sibling needs to be checked before
- // moving back up the tree.
- //
- while (StackData != Root) {
- assert(StackData->Parent != NULL);
- if (StackData->SiblingEntry.Next !=
- &(StackData->Parent->Children)) {
- StackData = LIST_VALUE(StackData->SiblingEntry.Next,
- STACK_DATA_ENTRY,
- SiblingEntry);
- Percent = (StackData->Count * 100) / TotalCount;
- if (Percent >= Threshold) {
- break;
- }
- }
- //
- // The sibling didn't work out. So move up the tree and look at
- // the parent's sibling. Update the indent string.
- //
- StackData = StackData->Parent;
- IndentStringLength -= PROFILER_STACK_INDENT_LENGTH;
- IndentString[IndentStringLength - 1] = '\0';
- }
- } while (StackData != Root);
- free(IndentString);
- return;
- }
- PMEMORY_POOL_ENTRY
- DbgrpGetMemoryPoolEntry (
- PLIST_ENTRY PoolListHead,
- PROFILER_MEMORY_TYPE ProfilerMemoryType
- )
- /*++
- Routine Description:
- This routine searches the given memory pool list and returns the entry for
- the given pool type.
- Arguments:
- PoolListHead - Supplies a pointer to the head of the memory pool list to be
- searched.
- ProfilerMemoryType - Supplies the profiler memory type of the memory pool
- being sought.
- Return Value:
- Returns a memory pool entry on success, or NULL on failure.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PPROFILER_MEMORY_POOL MemoryPool;
- PMEMORY_POOL_ENTRY MemoryPoolEntry;
- CurrentEntry = PoolListHead->Next;
- while (CurrentEntry != PoolListHead) {
- MemoryPoolEntry = LIST_VALUE(CurrentEntry,
- MEMORY_POOL_ENTRY,
- ListEntry);
- MemoryPool = &(MemoryPoolEntry->MemoryPool);
- if (MemoryPool->ProfilerMemoryType == ProfilerMemoryType) {
- return MemoryPoolEntry;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- return NULL;
- }
- PPROFILER_MEMORY_POOL_TAG_STATISTIC
- DbgrpGetTagStatistics (
- PMEMORY_POOL_ENTRY MemoryPoolEntry,
- ULONG Tag
- )
- /*++
- Routine Description:
- This routine searches the tag statistics in the given memory pool for those
- belonging to the given tag.
- Arguments:
- MemoryPoolEntry - Supplies a pointer to the memory pool entry to be
- searched.
- Tag - Supplies the tag identifying the statistics to be returned.
- Return Value:
- Returns a pointer to memory pool tag statistics on success, or NULL on
- failure.
- --*/
- {
- ULONG Index;
- PPROFILER_MEMORY_POOL_TAG_STATISTIC Statistic;
- ULONG TagCount;
- TagCount = MemoryPoolEntry->MemoryPool.TagCount;
- for (Index = 0; Index < TagCount; Index += 1) {
- Statistic = &(MemoryPoolEntry->TagStatistics[Index]);
- if (Statistic->Tag == Tag) {
- return Statistic;
- }
- }
- return NULL;
- }
- INT
- DbgrpDispatchStackProfilerCommand (
- PDEBUGGER_CONTEXT Context,
- PSTR *Arguments,
- ULONG ArgumentCount
- )
- /*++
- Routine Description:
- This routine handles a stack profiler command.
- Arguments:
- Context - Supplies a pointer to the application context.
- Arguments - Supplies an array of strings containing the arguments.
- ArgumentCount - Supplies the number of arguments in the Arguments array.
- Return Value:
- Returns TRUE on success, or FALSE on failure.
- --*/
- {
- PSTR AdvancedString;
- PROFILER_DISPLAY_REQUEST DisplayRequest;
- LONG Threshold;
- assert(strcasecmp(Arguments[0], "stack") == 0);
- if (ArgumentCount < 2) {
- DbgOut(STACK_PROFILER_USAGE);
- return EINVAL;
- }
- Threshold = 0;
- DisplayRequest = ProfilerDisplayOneTime;
- if (strcasecmp(Arguments[1], "start") == 0) {
- DisplayRequest = ProfilerDisplayStart;
- } else if (strcasecmp(Arguments[1], "stop") == 0) {
- DisplayRequest = ProfilerDisplayStop;
- } else if (strcasecmp(Arguments[1], "clear") == 0) {
- DisplayRequest = ProfilerDisplayClear;
- } else if (strcasecmp(Arguments[1], "dump") == 0) {
- DisplayRequest = ProfilerDisplayOneTime;
- } else if (strcasecmp(Arguments[1], "threshold") == 0) {
- DisplayRequest = ProfilerDisplayOneTimeThreshold;
- if (ArgumentCount < 3) {
- DbgOut("Error: Percentage argument expected.\n");
- return EINVAL;
- }
- Threshold = strtol(Arguments[2], &AdvancedString, 0);
- if (Arguments[2] == AdvancedString) {
- DbgOut("Error: Invalid argument %s. Unable to convert to a valid "
- "threshold value.\n",
- Arguments[2]);
- return EINVAL;
- }
- //
- // The threshold for the stack profiler should only be from 0 to 100.
- //
- if ((Threshold < 0) || (Threshold > 100)) {
- DbgOut("Error: Invalid threshold percentage specified. Valid "
- "values are between 0 and 100.\n");
- return EINVAL;
- }
- } else if (strcasecmp(Arguments[1], "help") == 0) {
- DbgOut(STACK_PROFILER_USAGE);
- return 0;
- } else {
- DbgOut("Error: Unknown stack profiler command '%s'.\n\n", Arguments[1]);
- DbgOut(STACK_PROFILER_USAGE);
- return EINVAL;
- }
- UiDisplayProfilerData(ProfilerDataTypeStack,
- DisplayRequest,
- (ULONG)Threshold);
- return 0;
- }
- INT
- DbgrpDispatchMemoryProfilerCommand (
- PDEBUGGER_CONTEXT Context,
- PSTR *Arguments,
- ULONG ArgumentCount
- )
- /*++
- Routine Description:
- This routine handles a memory profiler command.
- Arguments:
- Context - Supplies a pointer to the application context.
- Arguments - Supplies an array of strings containing the arguments.
- ArgumentCount - Supplies the number of arguments in the Arguments array.
- Return Value:
- 0 on success.
- Returns an error code on failure.
- --*/
- {
- PSTR AdvancedString;
- PROFILER_DISPLAY_REQUEST DisplayRequest;
- LONG Threshold;
- assert(strcasecmp(Arguments[0], "memory") == 0);
- if (ArgumentCount < 2) {
- DbgOut(MEMORY_PROFILER_USAGE);
- return EINVAL;
- }
- Threshold = 0;
- DisplayRequest = ProfilerDisplayOneTime;
- if (strcasecmp(Arguments[1], "start") == 0) {
- DisplayRequest = ProfilerDisplayStart;
- } else if (strcasecmp(Arguments[1], "delta") == 0) {
- DisplayRequest = ProfilerDisplayStartDelta;
- } else if (strcasecmp(Arguments[1], "stop") == 0) {
- DisplayRequest = ProfilerDisplayStop;
- } else if (strcasecmp(Arguments[1], "clear") == 0) {
- DisplayRequest = ProfilerDisplayStopDelta;
- } else if (strcasecmp(Arguments[1], "dump") == 0) {
- DisplayRequest = ProfilerDisplayOneTime;
- } else if (strcasecmp(Arguments[1], "threshold") == 0) {
- DisplayRequest = ProfilerDisplayOneTimeThreshold;
- if (ArgumentCount < 3) {
- DbgOut("Error: Active count threshold argument expected.\n");
- return EINVAL;
- }
- Threshold = strtol(Arguments[2], &AdvancedString, 0);
- if (Arguments[2] == AdvancedString) {
- DbgOut("Error: Invalid argument %s. Unable to convert to a valid "
- "threshold value.\n",
- Arguments[2]);
- return EINVAL;
- }
- } else if (strcasecmp(Arguments[1], "help") == 0) {
- DbgOut(MEMORY_PROFILER_USAGE);
- return 0;
- } else {
- DbgOut("Error: Unknown memory profiler command '%s'.\n\n",
- Arguments[1]);
- DbgOut(MEMORY_PROFILER_USAGE);
- return EINVAL;
- }
- UiDisplayProfilerData(ProfilerDataTypeMemory,
- DisplayRequest,
- (ULONG)Threshold);
- return 0;
- }
|