12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- 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;
- UINTN 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: %d.\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;
- UINTN 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);
- Status = STATUS_SUCCESS;
- if (Added == FALSE) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- KeLowerRunLevel(OldRunLevel);
- 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);
- Status = STATUS_SUCCESS;
- if (Added == FALSE) {
- Status = STATUS_BUFFER_TOO_SMALL;
- }
- KeLowerRunLevel(OldRunLevel);
- 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;
- }
|