1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- 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);
- 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;
- 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;
- IndentString = realloc(IndentString, IndentStringLength);
- 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;
- }
|