123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- profiler.c
- Abstract:
- This module implements system profiling support.
- Author:
- Chris Stevens 1-Jul-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include <minoca/kernel/kdebug.h>
- #include "spp.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define SP_ALLOCATION_TAG 0x21217053 // '!!pS'
- //
- // Define the length of the scratch buffer within the profiler buffer.
- //
- #define SCRATCH_BUFFER_LENGTH 200
- //
- // Define the size of the profiler buffer.
- //
- #define PROFILER_BUFFER_LENGTH (128 * 1024)
- //
- // Define the period between memory statistics updates, in microseoncds.
- //
- #define MEMORY_STATISTICS_TIMER_PERIOD (1000 * MICROSECONDS_PER_MILLISECOND)
- //
- // Define the number of buffers required to track memory profiling data.
- //
- #define MEMORY_BUFFER_COUNT 3
- //
- // Define the buffer size for a new process or thread.
- //
- #define PROFILER_PROCESS_INFORMATION_SIZE 1024
- #define PROFILER_THREAD_INFORMATION_SIZE 1024
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structures defines the system profiler's collection buffer.
- Members:
- Buffer - Stores a byte array of profiler data ready to be consumed. This is
- a ring buffer.
- ProducerIndex - Stores the index into the buffer that the data producer
- will write to next.
- ConsumerIndex - Stores the index into the buffer that the consumer will
- read from next.
- ConsumerStopIndex - Stores the index into the buffer that the consumer will
- read up to before completing a round of consuming.
- Scratch - Supplies a byte array used as a temporary holding place for data,
- allowing data collection to be performed with sequential writes before
- copying the data to the ring buffer.
- --*/
- typedef struct _PROFILER_BUFFER {
- BYTE Buffer[PROFILER_BUFFER_LENGTH];
- ULONG ProducerIndex;
- ULONG ConsumerIndex;
- ULONG ConsumerStopIndex;
- BYTE Scratch[SCRATCH_BUFFER_LENGTH];
- } PROFILER_BUFFER, *PPROFILER_BUFFER;
- /*++
- Structure Description:
- This structure defines a memory statistics collection buffer for the
- system profiler.
- Members:
- Buffer - Stores a byte array of memory statistics data.
- BufferSize - Stores the size of the buffer, in bytes.
- --*/
- typedef struct _MEMORY_BUFFER {
- BYTE *Buffer;
- ULONG BufferSize;
- ULONG ConsumerIndex;
- } MEMORY_BUFFER, *PMEMORY_BUFFER;
- /*++
- Structure Description:
- This structure defines the system's memory profiling state.
- Members:
- MemoryBuffers - Stores an array of points to memory buffers.
- ConsumerActive - Stores a boolean indicating whether or not the consumer
- is active.
- ConsumerIndex - Stores the index of the memory buffer that was last
- consumed or is actively being consumed.
- ReadyIndex - Stores the index of the next buffer from which the consumer
- should read.
- ProducerIndex - Stores the index of the next buffer to which the producer
- should write.
- Timer - Stores a pointer to the timer that controls production.
- ThreadAlive - Stores a boolean indicating whether the thread is alive or
- not.
- --*/
- typedef struct _MEMORY_PROFILER {
- MEMORY_BUFFER MemoryBuffers[MEMORY_BUFFER_COUNT];
- BOOL ConsumerActive;
- ULONG ConsumerIndex;
- ULONG ReadyIndex;
- ULONG ProducerIndex;
- PKTIMER Timer;
- volatile BOOL ThreadAlive;
- } MEMORY_PROFILER, *PMEMORY_PROFILER;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- BOOL
- SppWriteProfilerBuffer (
- PPROFILER_BUFFER ProfilerBuffer,
- BYTE *Data,
- ULONG Length
- );
- BOOL
- SppReadProfilerBuffer (
- PPROFILER_BUFFER ProfilerBuffer,
- BYTE *Data,
- PULONG DataLength
- );
- ULONG
- SppReadProfilerData (
- BYTE *Destination,
- BYTE *Source,
- ULONG ByteCount,
- PULONG BytesRemaining
- );
- KSTATUS
- SppInitializeStackSampling (
- VOID
- );
- VOID
- SppDestroyStackSampling (
- ULONG Phase
- );
- KSTATUS
- SppInitializeMemoryStatistics (
- VOID
- );
- VOID
- SppDestroyMemoryStatistics (
- ULONG Phase
- );
- VOID
- SppMemoryStatisticsThread (
- PVOID Parameter
- );
- KSTATUS
- SppInitializeThreadStatistics (
- VOID
- );
- VOID
- SppSendInitialProcesses (
- VOID
- );
- KSTATUS
- SppSendInitialThreads (
- PROCESS_ID ProcessId
- );
- VOID
- SppProcessNewProcess (
- PROCESS_ID ProcessId
- );
- VOID
- SppProcessNewThread (
- PROCESS_ID ProcessId,
- THREAD_ID ThreadId
- );
- VOID
- SppDestroyThreadStatistics (
- ULONG Phase
- );
- VOID
- SppCollectThreadStatistic (
- PKTHREAD Thread,
- PPROCESSOR_BLOCK Processor,
- SCHEDULER_REASON ScheduleOutReason
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Stores a value indicating whether or not profiling is enabled for system
- // initialization. Can be set with PROFILER_TYPE_FLAG_* values.
- //
- ULONG SpEarlyEnabledFlags = 0x0;
- //
- // Stores a value indicating which types of profiling are enabled.
- //
- ULONG SpEnabledFlags;
- //
- // Stores a pointer to a queued lock protecting access to the profiling status
- // variables.
- //
- PQUEUED_LOCK SpProfilingQueuedLock;
- //
- // Structures that store stack sampling data.
- //
- PPROFILER_BUFFER *SpStackSamplingArray;
- ULONG SpStackSamplingArraySize;
- //
- // Stores a pointer to a structure that tracks memory statistics profiling.
- //
- PMEMORY_PROFILER SpMemory;
- //
- // Structures that store thread statistics.
- //
- PPROFILER_BUFFER *SpThreadStatisticsArray;
- ULONG SpThreadStatisticsArraySize;
- PSP_COLLECT_THREAD_STATISTIC SpCollectThreadStatisticRoutine;
- PSP_PROCESS_NEW_PROCESS SpProcessNewProcessRoutine;
- PSP_PROCESS_NEW_THREAD SpProcessNewThreadRoutine;
- //
- // ------------------------------------------------------------------ Functions
- //
- VOID
- SpProfilerInterrupt (
- PTRAP_FRAME TrapFrame
- )
- /*++
- Routine Description:
- This routine handles periodic profiler interrupts, collecting information
- about the system for analysis.
- Arguments:
- TrapFrame - Supplies a pointer to the current trap frame.
- Return Value:
- None.
- --*/
- {
- PVOID *CallStack;
- ULONG CallStackSize;
- ULONG Processor;
- PVOID Scratch;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelHigh);
- //
- // Immediately return if stack sampling is not enabled. It may have been
- // turned off while this interrupt was pending.
- //
- if ((SpEnabledFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) == 0) {
- return;
- }
- //
- // Do nothing on interrupt replay if the trap frame is NULL.
- //
- if (TrapFrame == NULL) {
- return;
- }
- //
- // Do not collect data on processors that have not been initialized for
- // profiling.
- //
- Processor = KeGetCurrentProcessorNumber();
- if (Processor >= SpStackSamplingArraySize) {
- return;
- }
- //
- // Collect the stack data from the trap frame.
- //
- Scratch = SpStackSamplingArray[Processor]->Scratch;
- CallStackSize = SCRATCH_BUFFER_LENGTH - sizeof(UINTN);
- CallStack = (PVOID *)(Scratch + sizeof(UINTN));
- Status = SppArchGetKernelStackData(TrapFrame, CallStack, &CallStackSize);
- if (!KSUCCESS(Status)) {
- return;
- }
- ASSERT(CallStackSize != 0);
- CallStackSize += sizeof(UINTN);
- *((PUINTN)Scratch) = PROFILER_DATA_SENTINEL | CallStackSize;
- //
- // Write the data to the sampling buffer.
- //
- SppWriteProfilerBuffer(SpStackSamplingArray[Processor],
- (BYTE *)Scratch,
- CallStackSize);
- return;
- }
- VOID
- SpSendProfilingData (
- VOID
- )
- /*++
- Routine Description:
- This routine sends profiling data to any listening consumer. It is called
- periodically on each processor during the clock interrupt.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- ASSERT(KeGetRunLevel() >= RunLevelClock);
- //
- // Call out to the current profiling consumer to have that component ask
- // for the data.
- //
- KdSendProfilingData();
- return;
- }
- KSTATUS
- SpGetProfilerData (
- PPROFILER_NOTIFICATION ProfilerNotification,
- PULONG Flags
- )
- /*++
- Routine Description:
- This routine fills the provided profiler notification with profiling data.
- A profiler consumer should call this routine to obtain data to send over
- the wire. It is assumed here that consumers will serialize consumption.
- Arguments:
- ProfilerNotification - Supplies a pointer to the profiler notification that
- is to be filled in with profiling data.
- Flags - Supplies a pointer to the types of profiling data the caller wants
- to collect. Upon return, the flags for the returned data will be
- returned.
- Return Value:
- Status code.
- --*/
- {
- ULONG DataSize;
- PMEMORY_BUFFER MemoryBuffer;
- ULONG Processor;
- BOOL ReadMore;
- ULONG RemainingLength;
- ASSERT(Flags != NULL);
- ASSERT(*Flags != 0);
- //
- // Process the requested profiling data in a set order, removing each type
- // from the set of flags as it is processed.
- //
- if ((*Flags & PROFILER_TYPE_FLAG_STACK_SAMPLING) != 0) {
- Processor = KeGetCurrentProcessorNumber();
- ASSERT(Processor < SpStackSamplingArraySize);
- //
- // Fill the buffer with data from the current processor's stack
- // sampling data.
- //
- ReadMore = SppReadProfilerBuffer(
- SpStackSamplingArray[Processor],
- ProfilerNotification->Data,
- &ProfilerNotification->Header.DataSize);
- ProfilerNotification->Header.Type = ProfilerDataTypeStack;
- ProfilerNotification->Header.Processor = Processor;
- //
- // If no more data is available, it means that the consumer has read up
- // to the producer, or up to its stop point (the point the producer was
- // at when the consumer started).
- //
- if (ReadMore == FALSE) {
- *Flags &= ~PROFILER_TYPE_FLAG_STACK_SAMPLING;
- }
- } else if ((*Flags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) != 0) {
- //
- // If the consumer is not currently active, then get the next buffer to
- // consume, which is indicated by the ready index.
- //
- if (SpMemory->ConsumerActive == FALSE) {
- SpMemory->ConsumerIndex = SpMemory->ReadyIndex;
- SpMemory->ConsumerActive = TRUE;
- }
- //
- // Copy as much data as possible from the consumer buffer to the
- // profiler notification data buffer.
- //
- MemoryBuffer = &(SpMemory->MemoryBuffers[SpMemory->ConsumerIndex]);
- RemainingLength = MemoryBuffer->BufferSize -
- MemoryBuffer->ConsumerIndex;
- if (RemainingLength < ProfilerNotification->Header.DataSize) {
- DataSize = RemainingLength;
- } else {
- DataSize = ProfilerNotification->Header.DataSize;
- }
- if (DataSize != 0) {
- RtlCopyMemory(ProfilerNotification->Data,
- &(MemoryBuffer->Buffer[MemoryBuffer->ConsumerIndex]),
- DataSize);
- }
- MemoryBuffer->ConsumerIndex += DataSize;
- ProfilerNotification->Header.Type = ProfilerDataTypeMemory;
- ProfilerNotification->Header.Processor = KeGetCurrentProcessorNumber();
- ProfilerNotification->Header.DataSize = DataSize;
- //
- // Mark the consumer inactive if all the data was consumed.
- //
- if (MemoryBuffer->ConsumerIndex == MemoryBuffer->BufferSize) {
- SpMemory->ConsumerActive = FALSE;
- *Flags &= ~PROFILER_TYPE_FLAG_MEMORY_STATISTICS;
- }
- } else if ((*Flags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) != 0) {
- Processor = KeGetCurrentProcessorNumber();
- ASSERT(Processor < SpThreadStatisticsArraySize);
- //
- // Fill the buffer with data from the current processor's stack
- // sampling data.
- //
- ReadMore = SppReadProfilerBuffer(
- SpThreadStatisticsArray[Processor],
- ProfilerNotification->Data,
- &(ProfilerNotification->Header.DataSize));
- ProfilerNotification->Header.Type = ProfilerDataTypeThread;
- ProfilerNotification->Header.Processor = Processor;
- //
- // If no more data is available, it means that the consumer has read up
- // to the producer, or up to its stop point (the point the producer was
- // at when the consumer started).
- //
- if (ReadMore == FALSE) {
- *Flags &= ~PROFILER_TYPE_FLAG_THREAD_STATISTICS;
- }
- }
- return STATUS_SUCCESS;
- }
- ULONG
- SpGetProfilerDataStatus (
- VOID
- )
- /*++
- Routine Description:
- This routine determines if there is profiling data for the current
- processor that needs to be sent to a consumer.
- Arguments:
- None.
- Return Value:
- Returns a set of flags representing which types of profiling data are
- available. Returns zero if nothing is available.
- --*/
- {
- PPROFILER_BUFFER Buffer;
- ULONG Flags;
- ULONG Processor;
- ASSERT(KeGetRunLevel() >= RunLevelClock);
- if (SpEnabledFlags == 0) {
- return 0;
- }
- Flags = SpEnabledFlags;
- //
- // Determine if there is stack sampling data to send.
- //
- if ((Flags & PROFILER_TYPE_FLAG_STACK_SAMPLING) != 0) {
- //
- // If stack sampling is not yet initialized on this processor remove it
- // from the flags.
- //
- Processor = KeGetCurrentProcessorNumber();
- if (Processor >= SpStackSamplingArraySize) {
- Flags &= ~PROFILER_TYPE_FLAG_STACK_SAMPLING;
- //
- // Otherwise if the stack sampling buffer is empty, then remove it from
- // the flags.
- //
- // N.B. This access if safe because the stack sampling destruction code
- // waits for least one clock interrupt after disabling stack
- // sampling before destroying the global array.
- //
- } else {
- Buffer = SpStackSamplingArray[Processor];
- if (Buffer->ProducerIndex == Buffer->ConsumerIndex) {
- Flags &= ~PROFILER_TYPE_FLAG_STACK_SAMPLING;
- }
- }
- }
- //
- // Determine if there are memory statistics to send.
- //
- if ((Flags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) != 0) {
- //
- // There is no new data if the consumer index still equals the ready
- // index or the producer index.
- //
- if ((SpMemory->ConsumerIndex == SpMemory->ReadyIndex) ||
- (SpMemory->ConsumerIndex == SpMemory->ProducerIndex)) {
- Flags &= ~PROFILER_TYPE_FLAG_MEMORY_STATISTICS;
- }
- }
- if ((Flags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) != 0) {
- //
- // If thread statistics are not yet initialized on this processor
- // remove them from the flags.
- //
- Processor = KeGetCurrentProcessorNumber();
- if (Processor >= SpThreadStatisticsArraySize) {
- Flags &= ~PROFILER_TYPE_FLAG_THREAD_STATISTICS;
- //
- // Otherwise if the thread statistics buffer is empty, then remove it
- // from the flags.
- //
- // N.B. This access if safe because the thread statistics destruction
- // code waits for least one clock interrupt after disabling
- // profiling before destroying the global array.
- //
- } else {
- Buffer = SpThreadStatisticsArray[Processor];
- if (Buffer->ProducerIndex == Buffer->ConsumerIndex) {
- Flags &= ~PROFILER_TYPE_FLAG_THREAD_STATISTICS;
- }
- }
- }
- return Flags;
- }
- KSTATUS
- SpInitializeProfiler (
- VOID
- )
- /*++
- Routine Description:
- This routine initializes system profiling at processor start-up. It
- extends the profiling infrastructure as each processor comes online. If
- early profiling is not enabled, this routine just exits.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- ASSERT(KeGetRunLevel() <= RunLevelDispatch);
- ASSERT(KeGetCurrentProcessorNumber() == 0);
- //
- // Always initialize the profiling lock in case profiling gets enabled
- // later on.
- //
- SpProfilingQueuedLock = KeCreateQueuedLock();
- if (SpProfilingQueuedLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeProfilerEnd;
- }
- //
- // Do nothing more if early profiling is not enabled for any profiling
- // types.
- //
- if (SpEarlyEnabledFlags != 0) {
- KeAcquireQueuedLock(SpProfilingQueuedLock);
- Status = SppStartSystemProfiler(SpEarlyEnabledFlags);
- KeReleaseQueuedLock(SpProfilingQueuedLock);
- if (!KSUCCESS(Status)) {
- goto InitializeProfilerEnd;
- }
- }
- Status = STATUS_SUCCESS;
- InitializeProfilerEnd:
- return Status;
- }
- KSTATUS
- SppStartSystemProfiler (
- ULONG Flags
- )
- /*++
- Routine Description:
- This routine starts the system profiler. This routine must be called at low
- level. It assumes the profiler queued lock is held.
- Arguments:
- Flags - Supplies a set of flags representing the types of profiling that
- should be started.
- Return Value:
- Status code.
- --*/
- {
- ULONG InitializedFlags;
- ULONG NewFlags;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- //
- // The caller must specify flags.
- //
- if (Flags == 0) {
- return STATUS_INVALID_PARAMETER;
- }
- InitializedFlags = 0;
- //
- // Determine what new profiling types need to be started.
- //
- NewFlags = Flags & ~SpEnabledFlags;
- //
- // If all the desired profiling types are already active, then just exit.
- //
- if (NewFlags == 0) {
- Status = STATUS_SUCCESS;
- goto StartSystemProfilerEnd;
- }
- //
- // Initialize the system profiler for each of the new types.
- //
- if ((NewFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) != 0) {
- Status = SppInitializeStackSampling();
- if (!KSUCCESS(Status)) {
- goto StartSystemProfilerEnd;
- }
- InitializedFlags |= PROFILER_TYPE_FLAG_STACK_SAMPLING;
- }
- if ((NewFlags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) != 0) {
- Status = SppInitializeMemoryStatistics();
- if (!KSUCCESS(Status)) {
- goto StartSystemProfilerEnd;
- }
- InitializedFlags |= PROFILER_TYPE_FLAG_MEMORY_STATISTICS;
- }
- if ((NewFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) != 0) {
- Status = SppInitializeThreadStatistics();
- if (!KSUCCESS(Status)) {
- goto StartSystemProfilerEnd;
- }
- InitializedFlags |= PROFILER_TYPE_FLAG_THREAD_STATISTICS;
- }
- KeUpdateClockForProfiling(TRUE);
- Status = STATUS_SUCCESS;
- StartSystemProfilerEnd:
- if (!KSUCCESS(Status)) {
- if (InitializedFlags != 0) {
- SppStopSystemProfiler(InitializedFlags);
- }
- }
- return Status;
- }
- KSTATUS
- SppStopSystemProfiler (
- ULONG Flags
- )
- /*++
- Routine Description:
- This routine stops the system profiler and destroys the profiling data
- structures. This routine must be called at low level. It assumes the
- profiler queued lock is held.
- Arguments:
- Flags - Supplies a set of flags representing the types of profiling that
- should be stopped.
- Return Value:
- Status code.
- --*/
- {
- BOOL DelayRequired;
- ULONG DisableFlags;
- ULONG Index;
- ULONG *InterruptCounts;
- RUNLEVEL OldRunLevel;
- ULONG ProcessorCount;
- PROCESSOR_SET Processors;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- //
- // The caller must specify flags.
- //
- if (Flags == 0) {
- return STATUS_INVALID_PARAMETER;
- }
- //
- // Determine what profiling types need to be stopped.
- //
- DisableFlags = Flags & SpEnabledFlags;
- //
- // If profiling is already disabled for the requested profiling types, then
- // just exit.
- //
- if (DisableFlags == 0) {
- Status = STATUS_SUCCESS;
- goto StopSystemProfilerEnd;
- }
- //
- // Phase 0 destroy stops the system profiler for each type that needs to be
- // stopped.
- //
- if ((DisableFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) != 0) {
- SppDestroyStackSampling(0);
- }
- if ((DisableFlags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) != 0) {
- SppDestroyMemoryStatistics(0);
- }
- if ((DisableFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) != 0) {
- SppDestroyThreadStatistics(0);
- }
- //
- // Once phase zero destruction is complete, each profiler has stopped
- // producing data immediately, but another core may be in the middle of
- // consuming profiling data during its clock interrupt. Wait until each
- // processor has received the notice that profiling is now disabled and
- // then destroy each profiler's data structures. This is guaranteed after
- // the clock interrupt has incremented once. If an array cannot be
- // allocated for the processor counts, then just yield for a bit. It is not
- // good enough to just send an IPI-level interrupt to each core. This may
- // land on top of a clock interrupt in the middle of checking to see if
- // there is pending profiling data, which is not done with interrupts
- // disabled (i.e. the IPI-level interrupt running doesn't indicate the
- // other core is done with the data). As a result, this routine could run
- // through and release the buffers being observed by the other core.
- //
- ProcessorCount = KeGetActiveProcessorCount();
- if (ProcessorCount > 1) {
- DelayRequired = TRUE;
- InterruptCounts = MmAllocateNonPagedPool(ProcessorCount * sizeof(ULONG),
- SP_ALLOCATION_TAG);
- if (InterruptCounts != NULL) {
- for (Index = 0; Index < ProcessorCount; Index += 1) {
- InterruptCounts[Index] = KeGetClockInterruptCount(Index);
- }
- //
- // As some cores may have gone idle, send a clock IPI out to all of
- // them to make sure the interrupt count gets incremented.
- //
- Processors.Target = ProcessorTargetAll;
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- Status = HlSendIpi(IpiTypeClock, &Processors);
- KeLowerRunLevel(OldRunLevel);
- if (KSUCCESS(Status)) {
- for (Index = 0; Index < ProcessorCount; Index += 1) {
- while (KeGetClockInterruptCount(Index) <=
- InterruptCounts[Index]) {
- KeYield();
- }
- }
- DelayRequired = FALSE;
- }
- MmFreeNonPagedPool(InterruptCounts);
- }
- //
- // If the allocation or IPI failed above, wait a conservative second to
- // make sure all the cores are done consuming the profiler data.
- //
- if (DelayRequired != FALSE) {
- KeDelayExecution(FALSE, FALSE, MICROSECONDS_PER_SECOND);
- }
- }
- //
- // Phase 1 destroy releases any resources for each type of profiling that
- // was stopped in phase 0.
- //
- if ((DisableFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) != 0) {
- SppDestroyStackSampling(1);
- }
- if ((DisableFlags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) != 0) {
- SppDestroyMemoryStatistics(1);
- }
- if ((DisableFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) != 0) {
- SppDestroyThreadStatistics(1);
- }
- if (SpEnabledFlags == 0) {
- KeUpdateClockForProfiling(FALSE);
- }
- Status = STATUS_SUCCESS;
- StopSystemProfilerEnd:
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- BOOL
- SppWriteProfilerBuffer (
- PPROFILER_BUFFER ProfilerBuffer,
- BYTE *Data,
- ULONG DataLength
- )
- /*++
- Routine Description:
- This routine writes the supplied data to the profiler data buffer. If there
- is not enough room in the buffer, it just exits.
- Arguments:
- ProfilerBuffer - Supplies a pointer to a profiler data buffer to which the
- given data should be written.
- Data - Supplies an array of bytes to write to the profiler data buffer.
- DataLength - Supplies the length of the data array, in bytes.
- Return Value:
- TRUE if the data was successfully added to the buffer.
- FALSE if the data was dropped.
- --*/
- {
- ULONG AvailableLength;
- ULONG BufferIndex;
- ULONG ConsumerIndex;
- ULONG DataIndex;
- ULONG FirstWriteLength;
- ULONG ProducerIndex;
- BOOL Result;
- ULONG SecondWriteLength;
- ULONG WriteLength;
- ConsumerIndex = ProfilerBuffer->ConsumerIndex;
- ProducerIndex = ProfilerBuffer->ProducerIndex;
- //
- // If the producer's and consumer's indices are equal, then the buffer is
- // empty. Allow the producer to write up until the end of the buffer, being
- // careful to never completely fill the buffer to differentiate between an
- // empty buffer and a full buffer.
- //
- if (ProducerIndex == ConsumerIndex) {
- AvailableLength = PROFILER_BUFFER_LENGTH - 1;
- //
- // If the producer's index is greater than the consumer's, then two writes
- // may be necessary to fill the buffer. Account for wrapping when
- // calculating the available length.
- //
- } else if (ProducerIndex > ConsumerIndex) {
- AvailableLength = PROFILER_BUFFER_LENGTH - ProducerIndex;
- AvailableLength += (ConsumerIndex - 1);
- //
- // If the producer's index is less than the consumer's, then allow the
- // producer to write up until 1 less than the consumer's index.
- //
- } else {
- ASSERT(ProducerIndex < ConsumerIndex);
- AvailableLength = (ConsumerIndex - 1) - ProducerIndex;
- }
- //
- // If the available length is not enough for the data, exit.
- //
- if (AvailableLength < DataLength) {
- Result = FALSE;
- goto WriteProfilerBufferEnd;
- }
- //
- // Determine if the write needs to be broken into two operations.
- //
- if ((ProducerIndex + DataLength) > PROFILER_BUFFER_LENGTH) {
- FirstWriteLength = PROFILER_BUFFER_LENGTH - ProducerIndex;
- ASSERT(FirstWriteLength <= DataLength);
- SecondWriteLength = DataLength - FirstWriteLength;
- } else {
- FirstWriteLength = DataLength;
- SecondWriteLength = 0;
- }
- //
- // Write the data to the buffer.
- //
- DataIndex = 0;
- BufferIndex = ProducerIndex;
- WriteLength = FirstWriteLength;
- RtlCopyMemory(&(ProfilerBuffer->Buffer[BufferIndex]),
- &(Data[DataIndex]),
- WriteLength);
- if (SecondWriteLength != 0) {
- DataIndex = WriteLength;
- BufferIndex = 0;
- WriteLength = SecondWriteLength;
- RtlCopyMemory(&(ProfilerBuffer->Buffer[BufferIndex]),
- &(Data[DataIndex]),
- WriteLength);
- }
- //
- // Update the producer index.
- //
- ProducerIndex = BufferIndex + WriteLength;
- if (ProducerIndex == PROFILER_BUFFER_LENGTH) {
- ProfilerBuffer->ProducerIndex = 0;
- } else {
- ProfilerBuffer->ProducerIndex = ProducerIndex;
- }
- Result = TRUE;
- WriteProfilerBufferEnd:
- return Result;
- }
- BOOL
- SppReadProfilerBuffer (
- PPROFILER_BUFFER ProfilerBuffer,
- BYTE *Data,
- PULONG DataLength
- )
- /*++
- Routine Description:
- This routine reads up to the provided data length of bytes from the given
- profiler buffer. Upon return, the data length is modified to reflect the
- total number of bytes read. If there are no new bytes to read from the
- buffer, then a data length of zero is returned.
- Arguments:
- ProfilerBuffer - Supplies a pointer to a profiler data buffer from which
- up to data length bytes will be read.
- Data - Supplies an array of bytes that is to receive data from the profiler
- buffer.
- DataLength - Supplies the maximum number of bytes that can be read into the
- data buffer. Receives the total bytes read upon return.
- Return Value:
- Returns TRUE if there is more data to be read, or FALSE otherwise..
- --*/
- {
- ULONG AvailableLength;
- ULONG BytesRead;
- ULONG ConsumerIndex;
- ULONG ConsumerStopIndex;
- ULONG FirstReadLength;
- BOOL MoreDataAvailable;
- ULONG ProducerIndex;
- ULONG RemainingLength;
- ULONG SecondReadLength;
- ULONG TotalReadLength;
- ASSERT(ProfilerBuffer != NULL);
- ASSERT(Data != NULL);
- ASSERT(DataLength != NULL);
- ConsumerIndex = ProfilerBuffer->ConsumerIndex;
- ProducerIndex = ProfilerBuffer->ProducerIndex;
- ConsumerStopIndex = ProfilerBuffer->ConsumerStopIndex;
- SecondReadLength = 0;
- AvailableLength = *DataLength;
- *DataLength = 0;
- MoreDataAvailable = FALSE;
- //
- // If the stop index equals the consumer index, then advance it to
- // the producer index in order to gather all of the currently available
- // data. Do this so that the consumer will eventually complete when faced
- // with a speedy producer.
- //
- if (ConsumerIndex == ConsumerStopIndex) {
- ProfilerBuffer->ConsumerStopIndex = ProducerIndex;
- }
- //
- // If the producer's and consumer's indices are equal, then there are no
- // bytes to consume. The buffer is empty.
- //
- if (ProducerIndex == ConsumerIndex) {
- goto EmptyProfilerBufferEnd;
- //
- // If the producer is ahead of the consumer, then consume the buffer all
- // the way up to the producer's index or up to the provided buffer size.
- //
- } else if (ProducerIndex > ConsumerIndex) {
- FirstReadLength = ProducerIndex - ConsumerIndex;
- if (FirstReadLength > AvailableLength) {
- FirstReadLength = AvailableLength;
- }
- //
- // If the producer is behind the consumer, then two reads are required to
- // wrap around the circular buffer. Truncate based on the provided data
- // length.
- //
- } else {
- ASSERT(ProducerIndex < ConsumerIndex);
- FirstReadLength = PROFILER_BUFFER_LENGTH - ConsumerIndex;
- if (FirstReadLength > AvailableLength) {
- FirstReadLength = AvailableLength;
- } else {
- SecondReadLength = ProducerIndex;
- if ((FirstReadLength + SecondReadLength) > AvailableLength) {
- SecondReadLength = AvailableLength - FirstReadLength;
- }
- }
- }
- TotalReadLength = FirstReadLength + SecondReadLength;
- //
- // The provided data buffer should be large enough to fit the determined
- // reads.
- //
- ASSERT(AvailableLength >= TotalReadLength);
- //
- // Read the data out into the supplied buffer, making sure to read on the
- // profiler unit boundary, as marked by the sentinel.
- //
- RemainingLength = TotalReadLength;
- BytesRead = SppReadProfilerData(&(Data[0]),
- &(ProfilerBuffer->Buffer[ConsumerIndex]),
- FirstReadLength,
- &RemainingLength);
- if ((SecondReadLength != 0) && (BytesRead == FirstReadLength)) {
- ASSERT(RemainingLength == SecondReadLength);
- BytesRead = SppReadProfilerData(&(Data[FirstReadLength]),
- &(ProfilerBuffer->Buffer[0]),
- SecondReadLength,
- &RemainingLength);
- ASSERT(SecondReadLength == (BytesRead + RemainingLength));
- ConsumerIndex = BytesRead;
- } else {
- ConsumerIndex += BytesRead;
- }
- //
- // Update the data length based on how much data was read.
- //
- *DataLength = TotalReadLength - RemainingLength;
- //
- // Update the consumer index.
- //
- if (ConsumerIndex == PROFILER_BUFFER_LENGTH) {
- ProfilerBuffer->ConsumerIndex = 0;
- } else {
- ProfilerBuffer->ConsumerIndex = ConsumerIndex;
- }
- //
- // If the stop index has been reached with this read, let the caller know
- // that there is no more data to collect at this time.
- //
- if (ProfilerBuffer->ConsumerIndex != ProfilerBuffer->ConsumerStopIndex) {
- MoreDataAvailable = TRUE;
- }
- EmptyProfilerBufferEnd:
- return MoreDataAvailable;
- }
- ULONG
- SppReadProfilerData (
- BYTE *Destination,
- BYTE *Source,
- ULONG ByteCount,
- PULONG BytesRemaining
- )
- /*++
- Routine Description:
- This routine reads as many profiler data units as it can, up to the
- supplied byte count, making sure to never exceed the remaining available
- bytes.
- Arguments:
- Destination - Supplies a pointer to the destination data buffer.
- Source - Supplies a pointer to the source data buffer.
- ByteCount - Supplies the maximum number of bytes that should be read out of
- the source buffer.
- BytesRemaining - Supplies a pointer to the maximum number of bytes that
- can be read to the destination buffer. It is updated upon return.
- Return Value:
- Returns the number of bytes read by this routine.
- --*/
- {
- ULONG BytesRead;
- ULONG DestinationIndex;
- ULONG SourceIndex;
- ULONG Value;
- BytesRead = 0;
- DestinationIndex = 0;
- for (SourceIndex = 0; SourceIndex < ByteCount; SourceIndex += 1) {
- //
- // If the current byte is the start of the sentinel, check the length
- // of the next data packet and do not continue if it will not fit in
- // the destination buffer.
- //
- Value = *((PULONG)&(Source[SourceIndex]));
- if (IS_PROFILER_DATA_SENTINEL(Value) != FALSE) {
- if (GET_PROFILER_DATA_SIZE(Value) > *BytesRemaining) {
- break;
- }
- }
- Destination[DestinationIndex] = Source[SourceIndex];
- DestinationIndex += 1;
- *BytesRemaining -= 1;
- BytesRead += 1;
- }
- return BytesRead;
- }
- KSTATUS
- SppInitializeStackSampling (
- VOID
- )
- /*++
- Routine Description:
- This routine initializes the system's profiling data structures.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- ULONG AllocationSize;
- ULONG Index;
- ULONG ProcessorCount;
- PPROFILER_BUFFER ProfilerBuffer;
- PPROFILER_BUFFER *StackSamplingArray;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) == 0);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- ASSERT(SpStackSamplingArray == NULL);
- ASSERT(SpStackSamplingArraySize == 0);
- ProcessorCount = KeGetActiveProcessorCount();
- AllocationSize = ProcessorCount * sizeof(PPROFILER_BUFFER);
- StackSamplingArray = MmAllocateNonPagedPool(AllocationSize,
- SP_ALLOCATION_TAG);
- if (StackSamplingArray == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeProfilerEnd;
- }
- //
- // Now fill in the array with profiler buffers.
- //
- RtlZeroMemory(StackSamplingArray, AllocationSize);
- for (Index = 0; Index < ProcessorCount; Index += 1) {
- ProfilerBuffer = MmAllocateNonPagedPool(sizeof(PROFILER_BUFFER),
- SP_ALLOCATION_TAG);
- if (ProfilerBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeProfilerEnd;
- }
- RtlZeroMemory(ProfilerBuffer, sizeof(PROFILER_BUFFER));
- StackSamplingArray[Index] = ProfilerBuffer;
- }
- //
- // Start the timer and then mark the profiler as enabled and update the
- // stack sampling globals. This might cause some initial interrupts to skip
- // data collection, but that's OK.
- //
- Status = HlStartProfilerTimer();
- if (!KSUCCESS(Status)) {
- goto InitializeProfilerEnd;
- }
- SpStackSamplingArray = StackSamplingArray;
- SpStackSamplingArraySize = ProcessorCount;
- RtlMemoryBarrier();
- SpEnabledFlags |= PROFILER_TYPE_FLAG_STACK_SAMPLING;
- InitializeProfilerEnd:
- if (!KSUCCESS(Status)) {
- for (Index = 0; Index < ProcessorCount; Index += 1) {
- if (StackSamplingArray[Index] != NULL) {
- MmFreeNonPagedPool(StackSamplingArray[Index]);
- }
- }
- MmFreeNonPagedPool(StackSamplingArray);
- }
- return Status;
- }
- VOID
- SppDestroyStackSampling (
- ULONG Phase
- )
- /*++
- Routine Description:
- This routine tears down stack sampling by disabling the profiler timer and
- destroy the stack sampling data structures. Phase 0 stops the stack
- sampling profiler producers and consumers. Phase 1 cleans up resources.
- Arguments:
- Phase - Supplies the current phase of the destruction process.
- Return Value:
- None.
- --*/
- {
- ULONG Index;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- ASSERT(SpStackSamplingArray != NULL);
- ASSERT(SpStackSamplingArraySize != 0);
- if (Phase == 0) {
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) != 0);
- //
- // Disable stack sampling before disabling the profiler timer to
- // prevent any pending producer interrupts from touching the buffers
- // after they are released
- //
- SpEnabledFlags &= ~PROFILER_TYPE_FLAG_STACK_SAMPLING;
- //
- // Stop the profiler timer. Since the caller will wait for at least
- // one more clock interrupt, it is safe to proceed even though stopping
- // the timer doesn't guarantee the profiler interrupt will not run
- // again. It could be pending on another processor. The wait for the
- // clock interrupt will guarantee that the all high level and IPI
- // interrupts have completed.
- //
- HlStopProfilerTimer();
- } else {
- ASSERT(Phase == 1);
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_STACK_SAMPLING) == 0);
- //
- // Destroy the stack sampling array.
- //
- for (Index = 0; Index < SpStackSamplingArraySize; Index += 1) {
- if (SpStackSamplingArray[Index] != NULL) {
- MmFreeNonPagedPool(SpStackSamplingArray[Index]);
- }
- }
- MmFreeNonPagedPool(SpStackSamplingArray);
- SpStackSamplingArray = NULL;
- SpStackSamplingArraySize = 0;
- }
- return;
- }
- KSTATUS
- SppInitializeMemoryStatistics (
- VOID
- )
- /*++
- Routine Description:
- This routine initializes the structures and timers necessary for profiling
- system memory statistics.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- ULONGLONG Period;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- ASSERT(SpMemory == NULL);
- //
- // Allocate the memory profiler structure.
- //
- SpMemory = MmAllocateNonPagedPool(sizeof(MEMORY_PROFILER),
- SP_ALLOCATION_TAG);
- if (SpMemory == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeMemoryStatisticsEnd;
- }
- RtlZeroMemory(SpMemory, sizeof(MEMORY_PROFILER));
- ASSERT(SpMemory->ConsumerActive == FALSE);
- ASSERT(SpMemory->ReadyIndex == 0);
- ASSERT(SpMemory->ProducerIndex == 0);
- SpMemory->ConsumerIndex = MEMORY_BUFFER_COUNT - 1;
- //
- // Create the timer that will periodically trigger memory statistics.
- //
- SpMemory->Timer = KeCreateTimer(SP_ALLOCATION_TAG);
- if (SpMemory->Timer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeMemoryStatisticsEnd;
- }
- //
- // Queue the timer.
- //
- Period = KeConvertMicrosecondsToTimeTicks(MEMORY_STATISTICS_TIMER_PERIOD);
- Status = KeQueueTimer(SpMemory->Timer,
- TimerQueueSoft,
- 0,
- Period,
- 0,
- NULL);
- if (!KSUCCESS(Status)) {
- goto InitializeMemoryStatisticsEnd;
- }
- //
- // Create the worker thread, which will wait on the timer. Add an extra
- // reference because the destruction routine waits until this thread exits.
- //
- SpMemory->ThreadAlive = TRUE;
- Status = PsCreateKernelThread(SppMemoryStatisticsThread,
- NULL,
- "SppMemoryStatisticsThread");
- if (!KSUCCESS(Status)) {
- SpMemory->ThreadAlive = FALSE;
- goto InitializeMemoryStatisticsEnd;
- }
- //
- // Make sure everything above is complete before turning this on.
- //
- RtlMemoryBarrier();
- SpEnabledFlags |= PROFILER_TYPE_FLAG_MEMORY_STATISTICS;
- InitializeMemoryStatisticsEnd:
- if (!KSUCCESS(Status)) {
- if (SpMemory != NULL) {
- if (SpMemory->Timer != NULL) {
- KeDestroyTimer(SpMemory->Timer);
- }
- //
- // Thread creation should be the last point of failure.
- //
- ASSERT(SpMemory->ThreadAlive == FALSE);
- MmFreeNonPagedPool(SpMemory);
- SpMemory = NULL;
- }
- }
- return Status;
- }
- VOID
- SppDestroyMemoryStatistics (
- ULONG Phase
- )
- /*++
- Routine Description:
- This routine destroys the structures and timers used to profile system
- memory statistics. Phase 0 stops the memory profiler producers and
- consumers. Phase 1 cleans up resources.
- Arguments:
- Phase - Supplies the current phase of the destruction process.
- Return Value:
- None.
- --*/
- {
- ULONG Index;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- ASSERT(SpMemory != NULL);
- ASSERT(SpMemory->Timer != NULL);
- if (Phase == 0) {
- ASSERT(SpMemory->ThreadAlive != FALSE);
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) != 0);
- //
- // Disable the memory statistics profiler.
- //
- SpEnabledFlags &= ~PROFILER_TYPE_FLAG_MEMORY_STATISTICS;
- //
- // Cancel the timer. This is a periodic timer, so cancel should always
- // succeed.
- //
- Status = KeCancelTimer(SpMemory->Timer);
- ASSERT(KSUCCESS(Status));
- //
- // Queue the timer one more time in case the worker thread was in the
- // act of waiting when the timer was cancelled or was processing data.
- //
- Status = KeQueueTimer(SpMemory->Timer,
- TimerQueueSoftWake,
- 0,
- 0,
- 0,
- NULL);
- ASSERT(KSUCCESS(Status));
- //
- // Wait until the thread exits in order to be sure that it has
- // registered that profiling has been cancelled.
- //
- while (SpMemory->ThreadAlive != FALSE) {
- KeYield();
- }
- } else {
- ASSERT(Phase == 1);
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) == 0);
- ASSERT(SpMemory->ThreadAlive == FALSE);
- //
- // Destroy the timer.
- //
- KeDestroyTimer(SpMemory->Timer);
- //
- // Release any buffers that are holding pool statistics.
- //
- for (Index = 0; Index < MEMORY_BUFFER_COUNT; Index += 1) {
- if (SpMemory->MemoryBuffers[Index].Buffer != NULL) {
- MmFreeNonPagedPool(SpMemory->MemoryBuffers[Index].Buffer);
- }
- }
- MmFreeNonPagedPool(SpMemory);
- SpMemory = NULL;
- }
- return;
- }
- VOID
- SppMemoryStatisticsThread (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine is the workhorse for gathering memory statistics and writing
- them to a buffer than can then be consumed on the clock interrupt. It waits
- on the memory statistics timer before periodically collecting the
- statistics.
- Arguments:
- Parameter - Supplies a pointer supplied by the creator of the thread. This
- pointer is not used.
- Return Value:
- None.
- --*/
- {
- PVOID Buffer;
- ULONG BufferSize;
- ULONG Index;
- PMEMORY_BUFFER MemoryBuffer;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(SpMemory->ThreadAlive != FALSE);
- while (TRUE) {
- //
- // Wait for the memory statistics timer to expire.
- //
- ObWaitOnObject(SpMemory->Timer, 0, WAIT_TIME_INDEFINITE);
- //
- // Check to make sure memory statistics profiling is still enabled.
- //
- if ((SpEnabledFlags & PROFILER_TYPE_FLAG_MEMORY_STATISTICS) == 0) {
- break;
- }
- //
- // Call the memory manager to get the latest pool statistics. It will
- // pass back an appropriately sized buffer with all the statistics.
- //
- Status = MmGetPoolProfilerStatistics(&Buffer,
- &BufferSize,
- SP_ALLOCATION_TAG);
- if (!KSUCCESS(Status)) {
- continue;
- }
- //
- // Get the producer's memory buffer.
- //
- ASSERT(SpMemory->ProducerIndex < MEMORY_BUFFER_COUNT);
- MemoryBuffer = &(SpMemory->MemoryBuffers[SpMemory->ProducerIndex]);
- //
- // Destroy what is currently in the memory buffer.
- //
- if (MemoryBuffer->Buffer != NULL) {
- MmFreeNonPagedPool(MemoryBuffer->Buffer);
- }
- //
- // Reinitialize the buffer.
- //
- MemoryBuffer->Buffer = Buffer;
- MemoryBuffer->BufferSize = BufferSize;
- MemoryBuffer->ConsumerIndex = 0;
- //
- // Now that this is the latest and greatest memory information, point
- // the ready index at it. It doesn't matter that the ready index and
- // the producer index will temporarily be the same. There is a
- // guarantee that the producer will not produce again until it points
- // at a new buffer. This makes it safe for the consumer to just grab
- // the ready index.
- //
- SpMemory->ReadyIndex = SpMemory->ProducerIndex;
- //
- // Now search for the free buffer and make it the producer index. There
- // always has to be one free.
- //
- for (Index = 0; Index < MEMORY_BUFFER_COUNT; Index += 1) {
- if ((Index != SpMemory->ReadyIndex) &&
- (Index != SpMemory->ConsumerIndex)) {
- SpMemory->ProducerIndex = Index;
- break;
- }
- }
- ASSERT(SpMemory->ReadyIndex != SpMemory->ProducerIndex);
- }
- SpMemory->ThreadAlive = FALSE;
- return;
- }
- KSTATUS
- SppInitializeThreadStatistics (
- VOID
- )
- /*++
- Routine Description:
- This routine initializes the system's thread profiling data structures.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- ULONG AllocationSize;
- ULONG Index;
- RUNLEVEL OldRunLevel;
- ULONG ProcessorCount;
- ULONG ProcessorNumber;
- PPROFILER_BUFFER ProfilerBuffer;
- KSTATUS Status;
- SYSTEM_TIME SystemTime;
- PPROFILER_BUFFER *ThreadStatisticsArray;
- PROFILER_THREAD_TIME_COUNTER TimeCounterEvent;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) == 0);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- ASSERT(SpThreadStatisticsArray == NULL);
- ASSERT(SpThreadStatisticsArraySize == 0);
- ProcessorCount = KeGetActiveProcessorCount();
- AllocationSize = ProcessorCount * sizeof(PPROFILER_BUFFER);
- ThreadStatisticsArray = MmAllocateNonPagedPool(AllocationSize,
- SP_ALLOCATION_TAG);
- if (ThreadStatisticsArray == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeProfilerEnd;
- }
- //
- // Now fill in the array with profiler buffers.
- //
- RtlZeroMemory(ThreadStatisticsArray, AllocationSize);
- for (Index = 0; Index < ProcessorCount; Index += 1) {
- ProfilerBuffer = MmAllocateNonPagedPool(sizeof(PROFILER_BUFFER),
- SP_ALLOCATION_TAG);
- if (ProfilerBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeProfilerEnd;
- }
- RtlZeroMemory(ProfilerBuffer, sizeof(PROFILER_BUFFER));
- ThreadStatisticsArray[Index] = ProfilerBuffer;
- }
- SpThreadStatisticsArray = ThreadStatisticsArray;
- SpThreadStatisticsArraySize = ProcessorCount;
- //
- // Enable profiling by filling in the function pointer.
- //
- SpCollectThreadStatisticRoutine = SppCollectThreadStatistic;
- SpProcessNewProcessRoutine = SppProcessNewProcess;
- SpProcessNewThreadRoutine = SppProcessNewThread;
- RtlMemoryBarrier();
- SpEnabledFlags |= PROFILER_TYPE_FLAG_THREAD_STATISTICS;
- //
- // Raise to dispatch (so that no thread events are added on this processor)
- // and add the first event, a time counter synchronization event.
- //
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- ProcessorNumber = KeGetCurrentProcessorNumber();
- TimeCounterEvent.EventType = ProfilerThreadEventTimeCounter;
- TimeCounterEvent.TimeCounter = HlQueryTimeCounter();
- KeGetSystemTime(&SystemTime);
- TimeCounterEvent.SystemTimeSeconds = SystemTime.Seconds;
- TimeCounterEvent.SystemTimeNanoseconds = SystemTime.Nanoseconds;
- TimeCounterEvent.TimeCounterFrequency = HlQueryTimeCounterFrequency();
- SppWriteProfilerBuffer(SpThreadStatisticsArray[ProcessorNumber],
- (BYTE *)&TimeCounterEvent,
- sizeof(PROFILER_THREAD_TIME_COUNTER));
- KeLowerRunLevel(OldRunLevel);
- SppSendInitialProcesses();
- Status = STATUS_SUCCESS;
- InitializeProfilerEnd:
- if (!KSUCCESS(Status)) {
- for (Index = 0; Index < ProcessorCount; Index += 1) {
- if (ThreadStatisticsArray[Index] != NULL) {
- MmFreeNonPagedPool(ThreadStatisticsArray[Index]);
- }
- }
- MmFreeNonPagedPool(ThreadStatisticsArray);
- }
- return Status;
- }
- VOID
- SppSendInitialProcesses (
- VOID
- )
- /*++
- Routine Description:
- This routine sends the initial set of process and threads active on the
- system. This routine must be called at low level.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- BOOL Added;
- ULONG ConsumedSize;
- PPROFILER_THREAD_NEW_PROCESS Event;
- ULONG MaxNameSize;
- PSTR Name;
- ULONG NameSize;
- RUNLEVEL OldRunLevel;
- PPROCESS_INFORMATION Process;
- PPROCESS_INFORMATION ProcessList;
- ULONG ProcessListSize;
- ULONG ProcessorNumber;
- KSTATUS Status;
- KSTATUS ThreadStatus;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ProcessList = NULL;
- Status = PsGetAllProcessInformation(SP_ALLOCATION_TAG,
- (PVOID)&ProcessList,
- &ProcessListSize);
- if (!KSUCCESS(Status)) {
- goto SendInitialProcessesEnd;
- }
- ConsumedSize = 0;
- MaxNameSize = SCRATCH_BUFFER_LENGTH -
- FIELD_OFFSET(PROFILER_THREAD_NEW_PROCESS, Name);
- Process = ProcessList;
- while (ConsumedSize < ProcessListSize) {
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- ProcessorNumber = KeGetCurrentProcessorNumber();
- Event = (PVOID)(SpThreadStatisticsArray[ProcessorNumber]->Scratch);
- Event->EventType = ProfilerThreadEventNewProcess;
- NameSize = Process->NameLength * sizeof(CHAR);
- if (NameSize > MaxNameSize) {
- NameSize = MaxNameSize;
- }
- Event->StructureSize = sizeof(PROFILER_THREAD_NEW_PROCESS);
- if (NameSize != 0) {
- Event->StructureSize -= ANYSIZE_ARRAY * sizeof(CHAR);
- Event->StructureSize += NameSize;
- Name = (PSTR)((PVOID)Process + Process->NameOffset);
- RtlStringCopy(Event->Name, Name, NameSize);
- } else {
- Event->Name[0] = STRING_TERMINATOR;
- }
- Event->ProcessId = Process->ProcessId;
- Event->TimeCounter = 0;
- Added = SppWriteProfilerBuffer(SpThreadStatisticsArray[ProcessorNumber],
- (BYTE *)Event,
- Event->StructureSize);
- if (Added == FALSE) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- KeLowerRunLevel(OldRunLevel);
- ThreadStatus = SppSendInitialThreads(Process->ProcessId);
- if (!KSUCCESS(ThreadStatus)) {
- Status = ThreadStatus;
- }
- ConsumedSize += Process->StructureSize;
- ASSERT(ConsumedSize <= ProcessListSize);
- Process = (PPROCESS_INFORMATION)((PUCHAR)Process +
- Process->StructureSize);
- }
- Status = STATUS_SUCCESS;
- SendInitialProcessesEnd:
- if (ProcessList != NULL) {
- MmFreeNonPagedPool(ProcessList);
- }
- if (!KSUCCESS(Status)) {
- RtlDebugPrint("Profiler: Failed to send initial processes: %x.\n",
- Status);
- }
- return;
- }
- KSTATUS
- SppSendInitialThreads (
- PROCESS_ID ProcessId
- )
- /*++
- Routine Description:
- This routine sends the initial set of threads for the given process.
- This routine must be called at dispatch level.
- Arguments:
- ProcessId - Supplies the process ID of the threads to send.
- Return Value:
- None.
- --*/
- {
- BOOL Added;
- ULONG ConsumedSize;
- PPROFILER_THREAD_NEW_THREAD Event;
- ULONG MaxNameSize;
- ULONG NameSize;
- RUNLEVEL OldRunLevel;
- ULONG ProcessorNumber;
- KSTATUS Status;
- PTHREAD_INFORMATION Thread;
- PTHREAD_INFORMATION ThreadList;
- ULONG ThreadListSize;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- if ((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) == 0) {
- return STATUS_SUCCESS;
- }
- ThreadList = NULL;
- Status = PsGetThreadList(ProcessId,
- SP_ALLOCATION_TAG,
- (PVOID)&ThreadList,
- &ThreadListSize);
- if (!KSUCCESS(Status)) {
- goto SendInitialThreadsEnd;
- }
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- ProcessorNumber = KeGetCurrentProcessorNumber();
- Event = (PVOID)(SpThreadStatisticsArray[ProcessorNumber]->Scratch);
- ConsumedSize = 0;
- MaxNameSize = SCRATCH_BUFFER_LENGTH -
- FIELD_OFFSET(PROFILER_THREAD_NEW_THREAD, Name);
- Event->EventType = ProfilerThreadEventNewThread;
- Thread = ThreadList;
- while (ConsumedSize < ThreadListSize) {
- ASSERT(Thread->StructureSize >= sizeof(THREAD_INFORMATION));
- NameSize = Thread->StructureSize -
- FIELD_OFFSET(THREAD_INFORMATION, Name);
- if (NameSize > MaxNameSize) {
- NameSize = MaxNameSize;
- }
- Event->StructureSize = sizeof(PROFILER_THREAD_NEW_THREAD) -
- (ANYSIZE_ARRAY * sizeof(CHAR)) + NameSize;
- Event->ProcessId = ProcessId;
- Event->ThreadId = Thread->ThreadId;
- Event->TimeCounter = 0;
- RtlStringCopy(Event->Name, Thread->Name, NameSize);
- Added = SppWriteProfilerBuffer(SpThreadStatisticsArray[ProcessorNumber],
- (BYTE *)Event,
- Event->StructureSize);
- if (Added == FALSE) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- ConsumedSize += Thread->StructureSize;
- ASSERT(ConsumedSize <= ThreadListSize);
- Thread = (PTHREAD_INFORMATION)((PUCHAR)Thread + Thread->StructureSize);
- }
- KeLowerRunLevel(OldRunLevel);
- Status = STATUS_SUCCESS;
- SendInitialThreadsEnd:
- if (ThreadList != NULL) {
- MmFreeNonPagedPool(ThreadList);
- }
- return Status;
- }
- VOID
- SppProcessNewProcess (
- PROCESS_ID ProcessId
- )
- /*++
- Routine Description:
- This routine collects statistics on a created process.
- Arguments:
- ProcessId - Supplies the ID of the process being created.
- Return Value:
- None.
- --*/
- {
- BOOL Added;
- PPROFILER_THREAD_NEW_PROCESS Event;
- ULONG MaxNameSize;
- PSTR Name;
- ULONG NameSize;
- RUNLEVEL OldRunLevel;
- PPROCESS_INFORMATION Process;
- ULONG ProcessorNumber;
- ULONG ProcessSize;
- KSTATUS Status;
- if ((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) == 0) {
- return;
- }
- ProcessSize = PROFILER_PROCESS_INFORMATION_SIZE;
- Process = MmAllocateNonPagedPool(ProcessSize, SP_ALLOCATION_TAG);
- if (Process == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto ProcessNewProcessEnd;
- }
- Process->Version = PROCESS_INFORMATION_VERSION;
- Status = PsGetProcessInformation(ProcessId, Process, &ProcessSize);
- if (!KSUCCESS(Status)) {
- goto ProcessNewProcessEnd;
- }
- MaxNameSize = SCRATCH_BUFFER_LENGTH -
- FIELD_OFFSET(PROFILER_THREAD_NEW_PROCESS, Name);
- NameSize = Process->NameLength * sizeof(CHAR);
- if (NameSize > MaxNameSize) {
- NameSize = MaxNameSize;
- }
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- ProcessorNumber = KeGetCurrentProcessorNumber();
- Event = (PVOID)(SpThreadStatisticsArray[ProcessorNumber]->Scratch);
- Event->EventType = ProfilerThreadEventNewProcess;
- Event->StructureSize = sizeof(PROFILER_THREAD_NEW_PROCESS);
- if (NameSize != 0) {
- Event->StructureSize -= ANYSIZE_ARRAY * sizeof(CHAR);
- Event->StructureSize += NameSize;
- Name = (PSTR)((PVOID)Process + Process->NameOffset);
- RtlStringCopy(Event->Name, Name, NameSize);
- } else {
- Event->Name[0] = STRING_TERMINATOR;
- }
- Event->ProcessId = Process->ProcessId;
- Event->TimeCounter = HlQueryTimeCounter();
- Added = SppWriteProfilerBuffer(SpThreadStatisticsArray[ProcessorNumber],
- (BYTE *)Event,
- Event->StructureSize);
- if (Added == FALSE) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- KeLowerRunLevel(OldRunLevel);
- Status = STATUS_SUCCESS;
- ProcessNewProcessEnd:
- if (!KSUCCESS(Status)) {
- RtlDebugPrint("Warning: Unable to add profiling event for new "
- "process %d.\n",
- ProcessId);
- }
- if (Process != NULL) {
- MmFreeNonPagedPool(Process);
- }
- return;
- }
- VOID
- SppProcessNewThread (
- PROCESS_ID ProcessId,
- THREAD_ID ThreadId
- )
- /*++
- Routine Description:
- This routine collects statistics on a created thread.
- Arguments:
- ProcessId - Supplies the ID of the process creating the new thread.
- ThreadId - Supplies the ID of the new thread being created.
- Return Value:
- None.
- --*/
- {
- BOOL Added;
- PPROFILER_THREAD_NEW_THREAD Event;
- ULONG NameSize;
- RUNLEVEL OldRunLevel;
- ULONG ProcessorNumber;
- KSTATUS Status;
- PTHREAD_INFORMATION Thread;
- ULONG ThreadSize;
- if ((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) == 0) {
- return;
- }
- ThreadSize = PROFILER_THREAD_INFORMATION_SIZE;
- Thread = MmAllocateNonPagedPool(ThreadSize, SP_ALLOCATION_TAG);
- if (Thread == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto ProcessNewThreadEnd;
- }
- Status = PsGetThreadInformation(ProcessId, ThreadId, Thread, &ThreadSize);
- if (!KSUCCESS(Status)) {
- goto ProcessNewThreadEnd;
- }
- OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
- ProcessorNumber = KeGetCurrentProcessorNumber();
- Event = (PVOID)(SpThreadStatisticsArray[ProcessorNumber]->Scratch);
- Event->EventType = ProfilerThreadEventNewThread;
- ASSERT(Thread->StructureSize >= sizeof(THREAD_INFORMATION));
- NameSize = Thread->StructureSize -
- FIELD_OFFSET(THREAD_INFORMATION, Name);
- ASSERT(NameSize < ThreadSize);
- Event->StructureSize = sizeof(PROFILER_THREAD_NEW_THREAD) -
- (ANYSIZE_ARRAY * sizeof(CHAR)) + NameSize;
- Event->ProcessId = ProcessId;
- Event->ThreadId = Thread->ThreadId;
- Event->TimeCounter = HlQueryTimeCounter();
- RtlStringCopy(Event->Name, Thread->Name, NameSize);
- Added = SppWriteProfilerBuffer(SpThreadStatisticsArray[ProcessorNumber],
- (BYTE *)Event,
- Event->StructureSize);
- if (Added == FALSE) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- KeLowerRunLevel(OldRunLevel);
- Status = STATUS_SUCCESS;
- ProcessNewThreadEnd:
- if (!KSUCCESS(Status)) {
- RtlDebugPrint("Warning: Unable to add profiling event for new "
- "thread %d (Process %d).\n",
- ThreadId,
- ProcessId);
- }
- if (Thread != NULL) {
- MmFreeNonPagedPool(Thread);
- }
- return;
- }
- VOID
- SppDestroyThreadStatistics (
- ULONG Phase
- )
- /*++
- Routine Description:
- This routine tears down thread profiling. Phase 0 stops the thread
- statistics producers and consumers. Phase 1 cleans up resources.
- Arguments:
- Phase - Supplies the current phase of the destruction process.
- Return Value:
- None.
- --*/
- {
- ULONG Index;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- ASSERT(KeIsQueuedLockHeld(SpProfilingQueuedLock) != FALSE);
- ASSERT(SpThreadStatisticsArray != NULL);
- ASSERT(SpThreadStatisticsArraySize != 0);
- if (Phase == 0) {
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) != 0);
- //
- // Disable thread statistics before disabling the profiler function to
- // prevent any pending producers from touching the buffers after they
- // are released
- //
- SpEnabledFlags &= ~PROFILER_TYPE_FLAG_THREAD_STATISTICS;
- //
- // Clear the function pointer to officially take the profiling down.
- //
- SpCollectThreadStatisticRoutine = NULL;
- RtlMemoryBarrier();
- } else {
- ASSERT(Phase == 1);
- ASSERT((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) == 0);
- //
- // Destroy the stack sampling array.
- //
- for (Index = 0; Index < SpThreadStatisticsArraySize; Index += 1) {
- if (SpThreadStatisticsArray[Index] != NULL) {
- MmFreeNonPagedPool(SpThreadStatisticsArray[Index]);
- }
- }
- MmFreeNonPagedPool(SpThreadStatisticsArray);
- SpThreadStatisticsArray = NULL;
- SpThreadStatisticsArraySize = 0;
- }
- return;
- }
- VOID
- SppCollectThreadStatistic (
- PKTHREAD Thread,
- PPROCESSOR_BLOCK Processor,
- SCHEDULER_REASON ScheduleOutReason
- )
- /*++
- Routine Description:
- This routine collects statistics on a thread that is being scheduled out.
- This routine must be called at dispatch level inside the scheduler.
- Arguments:
- Thread - Supplies a pointer to the thread being scheduled out.
- Processor - Supplies a pointer to the executing processor block.
- ScheduleOutReason - Supplies the reason the thread is being scheduled out.
- Return Value:
- None.
- --*/
- {
- PPROFILER_CONTEXT_SWAP ContextSwap;
- ULONG ProcessorNumber;
- if ((SpEnabledFlags & PROFILER_TYPE_FLAG_THREAD_STATISTICS) == 0) {
- return;
- }
- //
- // Do not collect data on processors that have not been initialized for
- // profiling.
- //
- if (Processor->ProcessorNumber >= SpThreadStatisticsArraySize) {
- return;
- }
- ProcessorNumber = Processor->ProcessorNumber;
- ASSERT(sizeof(PROFILER_CONTEXT_SWAP) < SCRATCH_BUFFER_LENGTH);
- ContextSwap = (PVOID)(SpThreadStatisticsArray[ProcessorNumber]->Scratch);
- ContextSwap->EventType = ScheduleOutReason;
- ContextSwap->TimeCount = HlQueryTimeCounter();
- ContextSwap->BlockingQueue = (UINTN)NULL;
- if (ScheduleOutReason == SchedulerReasonThreadBlocking) {
- ContextSwap->BlockingQueue = (UINTN)(ObGetBlockingQueue(Thread));
- }
- ContextSwap->ThreadId = Thread->ThreadId;
- ContextSwap->ProcessId = Thread->OwningProcess->Identifiers.ProcessId;
- //
- // Write the data to the sampling buffer.
- //
- SppWriteProfilerBuffer(SpThreadStatisticsArray[ProcessorNumber],
- (BYTE *)ContextSwap,
- sizeof(PROFILER_CONTEXT_SWAP));
- return;
- }
|