931 lines
26 KiB
C
931 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
proflib.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the implementation of a rudimentry user-mode
|
||
profiler.
|
||
|
||
Usage:
|
||
|
||
There are 4 routines, RtlInitializeProfile, RtlStartProfile,
|
||
RtlStopProfile, and RtlAnalyzeProfile. To initialize profiling
|
||
invoke RtlInitializeProfile, this routine is only called once and
|
||
goes through the address space looking for code regions of images
|
||
and DLLs. To start profiling call RtlStartProfile. To stop
|
||
profiling call RtlStopProfile. Note that RtlStartProfile and
|
||
RtlStopProfile can be called repeatedly to profile only key
|
||
"hot spots", For example:
|
||
RtlStartProfile ();
|
||
hot spot...
|
||
RtlStopProfile ();
|
||
....
|
||
RtlStartProfile ();
|
||
hot spot...
|
||
RtlStopProfile ();
|
||
|
||
To analyze the results call RtlAnalyzeProfile. This too can
|
||
be called repeatedly (it stops profiling during the analysis
|
||
phase and does NOT restart profiling). It also does not
|
||
zero out the values after reporting.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 4-Oct-1990
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include "..\..\..\..\private\ntos\dll\ldrp.h"
|
||
|
||
NTSTATUS
|
||
InitializeKernelProfile ( VOID );
|
||
|
||
#define PAGE_SIZE 4096
|
||
|
||
typedef struct _PROFILE_BLOCK {
|
||
HANDLE Handle;
|
||
PVOID ImageBase; //actual base in image header
|
||
PULONG CodeStart;
|
||
ULONG CodeLength;
|
||
PULONG Buffer;
|
||
ULONG BufferSize;
|
||
ULONG TextNumber;
|
||
ULONG BucketSize;
|
||
PVOID MappedImageBase; //actual base where mapped locally.
|
||
PSZ ImageName;
|
||
} PROFILE_BLOCK;
|
||
|
||
|
||
#define MAX_PROFILE_COUNT 50
|
||
|
||
PROFILE_BLOCK ProfileObject[MAX_PROFILE_COUNT];
|
||
|
||
ULONG NumberOfProfileObjects = 0;
|
||
PIMAGE_DEBUG_INFO KernelDebugInfo;
|
||
|
||
//
|
||
// Image name to perform kernel mode analysis upon.
|
||
//
|
||
|
||
#define IMAGE_NAME "\\SystemRoot\\ntoskrnl.exe"
|
||
|
||
//
|
||
// Define map data file if the produced data file should be
|
||
// a mapped file (currently named "kernprof.dat").
|
||
//
|
||
|
||
// #define MAP_DATA_FILE
|
||
|
||
//
|
||
// Define map as image if the image to be profiled should be mapped
|
||
// as an image rather than as data.
|
||
//
|
||
|
||
// #define MAP_AS_IMAGE
|
||
|
||
#define MAX_PROFILE_COUNT 50
|
||
|
||
extern ULONG ProfInt;
|
||
|
||
NTSTATUS
|
||
RtlInitializeProfile (
|
||
IN BOOLEAN KernelToo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes profiling for the current process.
|
||
|
||
Arguments:
|
||
|
||
KernelToo - Set to TRUE if kernel code should be profiled as
|
||
well as user code.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the last NtCreateProfile.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status, LocalStatus;
|
||
HANDLE CurrentProcessHandle;
|
||
ULONG BufferSize;
|
||
PVOID ImageBase;
|
||
ULONG CodeLength;
|
||
PULONG Buffer;
|
||
PPEB Peb;
|
||
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
||
PSZ ImageName;
|
||
PLIST_ENTRY Next;
|
||
ULONG ExportSize, DebugSize;
|
||
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
|
||
PIMAGE_DEBUG_DIRECTORY DebugDirectory;
|
||
PIMAGE_DEBUG_INFO DebugInfo;
|
||
BOOLEAN PreviousPrivState;
|
||
|
||
//
|
||
// Locate all the executables in the address and create a
|
||
// seperate profile object for each one.
|
||
//
|
||
|
||
CurrentProcessHandle = NtCurrentProcess();
|
||
|
||
Peb = NtCurrentPeb();
|
||
|
||
Next = Peb->Ldr->InMemoryOrderModuleList.Flink;
|
||
while ( Next != &Peb->Ldr->InMemoryOrderModuleList) {
|
||
LdrDataTableEntry
|
||
= (PLDR_DATA_TABLE_ENTRY) (CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks));
|
||
|
||
ImageBase = LdrDataTableEntry->DllBase;
|
||
if ( Peb->ImageBaseAddress == ImageBase ) {
|
||
ImageName = "TheApplication";
|
||
} else {
|
||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
|
||
ImageBase,
|
||
TRUE,
|
||
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
||
&ExportSize);
|
||
|
||
ImageName = (PSZ)((ULONG)ImageBase + ExportDirectory->Name);
|
||
}
|
||
if (NumberOfProfileObjects > MAX_PROFILE_COUNT) {
|
||
break;
|
||
}
|
||
|
||
ProfileObject[NumberOfProfileObjects].ImageBase = ImageBase;
|
||
ProfileObject[NumberOfProfileObjects].ImageName = ImageName;
|
||
ProfileObject[NumberOfProfileObjects].MappedImageBase = ImageBase;
|
||
|
||
//
|
||
// Locate the code range and start profiling.
|
||
//
|
||
|
||
DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData(
|
||
ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_DEBUG, &DebugSize);
|
||
|
||
if (!DebugDirectory) {
|
||
DbgPrint ("RtlInitializeProfile : No debug directory\n");
|
||
return STATUS_INVALID_IMAGE_FORMAT;
|
||
}
|
||
|
||
DebugInfo = (PIMAGE_DEBUG_INFO)((ULONG)ImageBase + DebugDirectory->AddressOfRawData);
|
||
|
||
ProfileObject[NumberOfProfileObjects].CodeStart = (PULONG)((ULONG)ImageBase + DebugInfo->RvaToFirstByteOfCode);
|
||
CodeLength = (DebugInfo->RvaToLastByteOfCode - DebugInfo->RvaToFirstByteOfCode) - 1;
|
||
ProfileObject[NumberOfProfileObjects].CodeLength = CodeLength;
|
||
|
||
ProfileObject[NumberOfProfileObjects].TextNumber = 1;
|
||
|
||
//
|
||
// Analyze the size of the code and create a reasonably sized
|
||
// profile object.
|
||
//
|
||
|
||
BufferSize = (CodeLength >> 1) + 4;
|
||
Buffer = NULL;
|
||
|
||
status = NtAllocateVirtualMemory (CurrentProcessHandle,
|
||
(PVOID *)&Buffer,
|
||
0,
|
||
&BufferSize,
|
||
MEM_RESERVE | MEM_COMMIT,
|
||
PAGE_READWRITE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint ("alloc VM failed %lx\n",status);
|
||
return status;
|
||
}
|
||
|
||
|
||
status = RtlAdjustPrivilege(
|
||
SE_PROF_SINGLE_PROCESS_PRIVILEGE,
|
||
TRUE, //Enable
|
||
FALSE, //not impersonating
|
||
&PreviousPrivState //Remember if it will need to be cleared
|
||
);
|
||
|
||
if (!NT_SUCCESS(status) || status == STATUS_NOT_ALL_ASSIGNED) {
|
||
DbgPrint("Enable system profile privilege failed - status 0x%lx\n",
|
||
status);
|
||
}
|
||
|
||
|
||
ProfileObject[NumberOfProfileObjects].Buffer = Buffer;
|
||
ProfileObject[NumberOfProfileObjects].BufferSize = BufferSize;
|
||
ProfileObject[NumberOfProfileObjects].BucketSize = 3;
|
||
|
||
status = NtCreateProfile (
|
||
&ProfileObject[NumberOfProfileObjects].Handle,
|
||
CurrentProcessHandle,
|
||
ProfileObject[NumberOfProfileObjects].CodeStart,
|
||
CodeLength,
|
||
ProfileObject[NumberOfProfileObjects].BucketSize,
|
||
ProfileObject[NumberOfProfileObjects].Buffer ,
|
||
ProfileObject[NumberOfProfileObjects].BufferSize,
|
||
ProfileTime,
|
||
(KAFFINITY)-1);
|
||
|
||
if (PreviousPrivState == FALSE) {
|
||
LocalStatus = RtlAdjustPrivilege(
|
||
SE_PROF_SINGLE_PROCESS_PRIVILEGE,
|
||
FALSE, //Disable
|
||
FALSE, //not impersonating
|
||
&PreviousPrivState //Don't care if it was already enabled
|
||
);
|
||
if (!NT_SUCCESS(LocalStatus) || LocalStatus == STATUS_NOT_ALL_ASSIGNED) {
|
||
DbgPrint("Disable system profile privilege failed - status 0x%lx\n",
|
||
LocalStatus);
|
||
}
|
||
}
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgPrint("create profile %x failed - status %lx\n",
|
||
ProfileObject[NumberOfProfileObjects].ImageName,status);
|
||
return status;
|
||
}
|
||
|
||
NumberOfProfileObjects += 1;
|
||
|
||
Next = Next->Flink;
|
||
}
|
||
|
||
if (KernelToo) {
|
||
|
||
if (NumberOfProfileObjects > MAX_PROFILE_COUNT) {
|
||
return status;
|
||
}
|
||
status = InitializeKernelProfile();
|
||
}
|
||
return status;
|
||
|
||
}
|
||
NTSTATUS
|
||
InitializeKernelProfile (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes profiling for the kernel for the
|
||
current process.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the last NtCreateProfile.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//BUGBUG daveh I think that the new working set size calculation is
|
||
// generating the number of pages, when the api expects
|
||
// the number of bytes.
|
||
|
||
STRING Name3;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
HANDLE FileHandle, KernelSection;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
PVOID ImageBase;
|
||
ULONG ViewSize;
|
||
ULONG CodeLength;
|
||
NTSTATUS status, LocalStatus;
|
||
HANDLE CurrentProcessHandle;
|
||
QUOTA_LIMITS QuotaLimits;
|
||
PVOID Buffer;
|
||
ULONG Cells;
|
||
ULONG BucketSize;
|
||
UNICODE_STRING Unicode;
|
||
ULONG DebugSize;
|
||
PVOID KernelBase;
|
||
PIMAGE_NT_HEADERS KernelNtHeaders;
|
||
PIMAGE_DEBUG_DIRECTORY DebugDirectory;
|
||
BOOLEAN PreviousPrivState;
|
||
|
||
RtlInitString (&Name3, IMAGE_NAME);
|
||
CurrentProcessHandle = NtCurrentProcess();
|
||
|
||
status = RtlAnsiStringToUnicodeString(&Unicode,(PANSI_STRING)&Name3,TRUE);
|
||
ASSERT(NT_SUCCESS(status));
|
||
InitializeObjectAttributes( &ObjectAttributes,
|
||
&Unicode,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL );
|
||
|
||
//
|
||
// Open the file as readable and executable.
|
||
//
|
||
|
||
status = NtOpenFile ( &FileHandle,
|
||
FILE_READ_DATA | FILE_EXECUTE,
|
||
&ObjectAttributes,
|
||
&IoStatus,
|
||
FILE_SHARE_READ,
|
||
0L);
|
||
RtlFreeUnicodeString(&Unicode);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint("open file failed status %lx\n", status);
|
||
NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
|
||
}
|
||
|
||
InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
|
||
|
||
//
|
||
// For normal images they would be mapped as an image, but
|
||
// the kernel has no debug section (as yet) information, hence it
|
||
// must be mapped as a file.
|
||
//
|
||
|
||
status = NtCreateSection (&KernelSection,
|
||
SECTION_MAP_EXECUTE,
|
||
&ObjectAttributes,
|
||
0,
|
||
PAGE_READONLY,
|
||
SEC_IMAGE,
|
||
FileHandle);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint("create image section failed status %lx\n", status);
|
||
return(status);
|
||
}
|
||
|
||
ViewSize = 0;
|
||
|
||
//
|
||
// Map a view of the section into the address space.
|
||
//
|
||
|
||
KernelBase = NULL;
|
||
|
||
status = NtMapViewOfSection (KernelSection,
|
||
CurrentProcessHandle,
|
||
(PVOID *)&KernelBase,
|
||
0L,
|
||
0,
|
||
NULL,
|
||
&ViewSize,
|
||
ViewUnmap,
|
||
0,
|
||
PAGE_EXECUTE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
if (status != STATUS_IMAGE_NOT_AT_BASE) {
|
||
DbgPrint("map section status %lx base %lx size %lx\n", status,
|
||
KernelBase, ViewSize);
|
||
}
|
||
}
|
||
|
||
KernelNtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(KernelBase);
|
||
|
||
ImageBase = (PVOID)KernelNtHeaders->OptionalHeader.ImageBase;
|
||
|
||
DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData(
|
||
KernelBase, TRUE, IMAGE_DIRECTORY_ENTRY_DEBUG, &DebugSize);
|
||
|
||
if (!DebugDirectory) {
|
||
DbgPrint("InitializeKernelProfile : No debug directory\n");
|
||
return STATUS_INVALID_IMAGE_FORMAT;
|
||
}
|
||
|
||
KernelDebugInfo = (PIMAGE_DEBUG_INFO)((ULONG)KernelBase + DebugDirectory->AddressOfRawData);
|
||
CodeLength = (KernelDebugInfo->RvaToLastByteOfCode - KernelDebugInfo->RvaToFirstByteOfCode) -1;
|
||
|
||
//
|
||
// Just create a 512K byte buffer.
|
||
//
|
||
|
||
ViewSize = 1024 * 512;
|
||
Buffer = NULL;
|
||
|
||
status = NtAllocateVirtualMemory (CurrentProcessHandle,
|
||
(PVOID *)&Buffer,
|
||
0,
|
||
&ViewSize,
|
||
MEM_RESERVE | MEM_COMMIT,
|
||
PAGE_READWRITE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint ("alloc VM failed %lx\n",status);
|
||
NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// Calculate the bucket size for the profile.
|
||
//
|
||
|
||
Cells = ((CodeLength / (ViewSize >> 2)) >> 2);
|
||
BucketSize = 2;
|
||
|
||
while (Cells != 0) {
|
||
Cells = Cells >> 1;
|
||
BucketSize += 1;
|
||
}
|
||
|
||
ProfileObject[NumberOfProfileObjects].Buffer = Buffer;
|
||
ProfileObject[NumberOfProfileObjects].MappedImageBase = KernelBase;
|
||
ProfileObject[NumberOfProfileObjects].BufferSize = 1 + (CodeLength >> (BucketSize - 2));
|
||
ProfileObject[NumberOfProfileObjects].CodeStart = (PULONG)((ULONG)ImageBase + KernelDebugInfo->RvaToFirstByteOfCode);
|
||
ProfileObject[NumberOfProfileObjects].CodeLength = CodeLength;
|
||
ProfileObject[NumberOfProfileObjects].TextNumber = 1;
|
||
ProfileObject[NumberOfProfileObjects].ImageBase = ImageBase;
|
||
ProfileObject[NumberOfProfileObjects].ImageName = "ntoskrnl";
|
||
ProfileObject[NumberOfProfileObjects].BucketSize = BucketSize;
|
||
|
||
|
||
//
|
||
// Increase the working set to lock down a bigger buffer.
|
||
//
|
||
|
||
status = NtQueryInformationProcess (CurrentProcessHandle,
|
||
ProcessQuotaLimits,
|
||
&QuotaLimits,
|
||
sizeof(QUOTA_LIMITS),
|
||
NULL );
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint ("query process info failed %lx\n",status);
|
||
NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
|
||
}
|
||
|
||
QuotaLimits.MaximumWorkingSetSize += ViewSize / PAGE_SIZE;
|
||
QuotaLimits.MinimumWorkingSetSize += ViewSize / PAGE_SIZE;
|
||
|
||
status = NtSetInformationProcess (CurrentProcessHandle,
|
||
ProcessQuotaLimits,
|
||
&QuotaLimits,
|
||
sizeof(QUOTA_LIMITS));
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint ("setting working set failed %lx\n",status);
|
||
return status;
|
||
}
|
||
|
||
status = RtlAdjustPrivilege(
|
||
SE_PROF_SINGLE_PROCESS_PRIVILEGE,
|
||
TRUE, //Enable
|
||
FALSE, //not impersonating
|
||
&PreviousPrivState //Remember if it will need to be cleared
|
||
);
|
||
|
||
if (!NT_SUCCESS(status) || status == STATUS_NOT_ALL_ASSIGNED) {
|
||
DbgPrint("Enable process profile privilege failed - status 0x%lx\n",
|
||
status);
|
||
}
|
||
|
||
status = NtCreateProfile (
|
||
&ProfileObject[NumberOfProfileObjects].Handle,
|
||
CurrentProcessHandle,
|
||
ProfileObject[NumberOfProfileObjects].CodeStart,
|
||
CodeLength,
|
||
ProfileObject[NumberOfProfileObjects].BucketSize,
|
||
ProfileObject[NumberOfProfileObjects].Buffer ,
|
||
ProfileObject[NumberOfProfileObjects].BufferSize,
|
||
ProfileTime,
|
||
(KAFFINITY)-1);
|
||
|
||
if (PreviousPrivState == FALSE) {
|
||
LocalStatus = RtlAdjustPrivilege(
|
||
SE_PROF_SINGLE_PROCESS_PRIVILEGE,
|
||
FALSE, //Disable
|
||
FALSE, //not impersonating
|
||
&PreviousPrivState //Don't care if it was already enabled
|
||
);
|
||
if (!NT_SUCCESS(LocalStatus) || LocalStatus == STATUS_NOT_ALL_ASSIGNED) {
|
||
DbgPrint("Disable system profile privilege failed - status 0x%lx\n",
|
||
LocalStatus);
|
||
}
|
||
}
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgPrint("create kernel profile %s failed - status %lx\n",
|
||
ProfileObject[NumberOfProfileObjects].ImageName,status);
|
||
}
|
||
|
||
NumberOfProfileObjects += 1;
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
RtlpWriteProfileLine(
|
||
IN HANDLE ProfileHandle,
|
||
IN PSZ Line,
|
||
IN int nbytes
|
||
)
|
||
{
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
NtWriteFile(
|
||
ProfileHandle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
Line,
|
||
(ULONG)nbytes,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
|
||
HANDLE
|
||
RtlpOpenProfileOutputFile()
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
UNICODE_STRING FileName;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
L"\\profile.out",
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
return NULL;
|
||
}
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
Status = NtCreateFile(
|
||
&Handle,
|
||
FILE_APPEND_DATA | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
FILE_SHARE_READ,
|
||
FILE_OPEN_IF,
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL,
|
||
0L
|
||
);
|
||
|
||
RtlFreeHeap(RtlProcessHeap(),0,FreeBuffer);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return NULL;
|
||
}
|
||
|
||
return Handle;
|
||
}
|
||
|
||
VOID
|
||
RtlpDeleteProfileOutputFile()
|
||
{
|
||
NTSTATUS Status;
|
||
OBJECT_ATTRIBUTES Obja;
|
||
HANDLE Handle;
|
||
UNICODE_STRING FileName;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_DISPOSITION_INFORMATION Disposition;
|
||
BOOLEAN TranslationStatus;
|
||
RTL_RELATIVE_NAME RelativeName;
|
||
PVOID FreeBuffer;
|
||
|
||
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
||
L"\\profile.out",
|
||
&FileName,
|
||
NULL,
|
||
&RelativeName
|
||
);
|
||
|
||
if ( !TranslationStatus ) {
|
||
return;
|
||
}
|
||
|
||
FreeBuffer = FileName.Buffer;
|
||
|
||
if ( RelativeName.RelativeName.Length ) {
|
||
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
||
}
|
||
else {
|
||
RelativeName.ContainingDirectory = NULL;
|
||
}
|
||
|
||
InitializeObjectAttributes(
|
||
&Obja,
|
||
&FileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
RelativeName.ContainingDirectory,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Open the file for delete access
|
||
//
|
||
|
||
Status = NtOpenFile(
|
||
&Handle,
|
||
(ACCESS_MASK)DELETE | SYNCHRONIZE,
|
||
&Obja,
|
||
&IoStatusBlock,
|
||
FILE_SHARE_DELETE,
|
||
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
|
||
);
|
||
RtlFreeHeap(RtlProcessHeap(),0,FreeBuffer);
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Delete the file
|
||
//
|
||
Disposition.DeleteFile = TRUE;
|
||
|
||
Status = NtSetInformationFile(
|
||
Handle,
|
||
&IoStatusBlock,
|
||
&Disposition,
|
||
sizeof(Disposition),
|
||
FileDispositionInformation
|
||
);
|
||
|
||
NtClose(Handle);
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RtlStartProfile (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine starts all profile objects which have been initialized.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the last NtStartProfile.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
NTSTATUS status;
|
||
QUOTA_LIMITS QuotaLimits;
|
||
|
||
NtSetIntervalProfile(ProfInt,ProfileTime);
|
||
RtlpDeleteProfileOutputFile();
|
||
|
||
for (i = 0; i < NumberOfProfileObjects; i++) {
|
||
|
||
status = NtStartProfile (ProfileObject[i].Handle);
|
||
|
||
if (status == STATUS_WORKING_SET_QUOTA) {
|
||
|
||
//
|
||
// Increase the working set to lock down a bigger buffer.
|
||
//
|
||
|
||
status = NtQueryInformationProcess (NtCurrentProcess(),
|
||
ProcessQuotaLimits,
|
||
&QuotaLimits,
|
||
sizeof(QUOTA_LIMITS),
|
||
NULL );
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint ("query process info failed %lx\n",status);
|
||
return status;
|
||
|
||
}
|
||
|
||
QuotaLimits.MaximumWorkingSetSize +=
|
||
10 * PAGE_SIZE + ProfileObject[i].BufferSize;
|
||
QuotaLimits.MinimumWorkingSetSize +=
|
||
10 * PAGE_SIZE + ProfileObject[i].BufferSize;
|
||
|
||
status = NtSetInformationProcess (NtCurrentProcess(),
|
||
ProcessQuotaLimits,
|
||
&QuotaLimits,
|
||
sizeof(QUOTA_LIMITS));
|
||
if (!NT_SUCCESS(status)) {
|
||
DbgPrint ("setting working set failed %lx\n",status);
|
||
return status;
|
||
}
|
||
status = NtStartProfile (ProfileObject[i].Handle);
|
||
}
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgPrint("start profile %s failed - status %lx\n",
|
||
ProfileObject[i].ImageName, status);
|
||
return status;
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
NTSTATUS
|
||
RtlStopProfile (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine stops all profile objects which have been initialized.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Returns the status of the last NtStopProfile.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
NTSTATUS status;
|
||
|
||
for (i = 0; i < NumberOfProfileObjects; i++) {
|
||
status = NtStopProfile (ProfileObject[i].Handle);
|
||
if (status != STATUS_SUCCESS) {
|
||
DbgPrint("stop profile %s failed - status %lx\n",
|
||
ProfileObject[i].ImageName,status);
|
||
return status;
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
RtlAnalyzeProfile (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the analysis of all the profile buffers and
|
||
correlates hits to the appropriate symbol table.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
RTL_SYMBOL_INFORMATION ThisSymbol;
|
||
RTL_SYMBOL_INFORMATION LastSymbol;
|
||
ULONG CountAtSymbol;
|
||
NTSTATUS Status;
|
||
ULONG Va;
|
||
HANDLE ProfileHandle;
|
||
CHAR Line[512];
|
||
int i,n;
|
||
PULONG Buffer, BufferEnd, Counter;
|
||
|
||
|
||
ProfileHandle = RtlpOpenProfileOutputFile();
|
||
ASSERT(ProfileHandle);
|
||
|
||
for (i = 0; i < NumberOfProfileObjects; i++) {
|
||
Status = NtStopProfile (ProfileObject[i].Handle);
|
||
}
|
||
|
||
|
||
//
|
||
// The new profiler
|
||
//
|
||
|
||
for (i = 0; i < NumberOfProfileObjects; i++) {
|
||
|
||
LastSymbol.Value = 0;
|
||
CountAtSymbol = 0;
|
||
|
||
//
|
||
// Sum the total number of cells written.
|
||
//
|
||
|
||
BufferEnd = ProfileObject[i].Buffer + (
|
||
ProfileObject[i].BufferSize / sizeof(ULONG));
|
||
Buffer = ProfileObject[i].Buffer;
|
||
|
||
for ( Counter = Buffer; Counter < BufferEnd; Counter += 1 ) {
|
||
if ( *Counter ) {
|
||
|
||
//
|
||
// Now we have an an address relative to the buffer
|
||
// base.
|
||
//
|
||
|
||
Va = (ULONG)((PUCHAR)Counter - (PUCHAR)Buffer);
|
||
Va = Va * ( 1 << (ProfileObject[i].BucketSize - 2));
|
||
|
||
//
|
||
// Add in the image base and the base of the
|
||
// code to get the Va in the image
|
||
//
|
||
|
||
Va = Va + (ULONG)ProfileObject[i].CodeStart;
|
||
|
||
Status = RtlLookupSymbolByAddress(
|
||
ProfileObject[i].ImageBase,
|
||
NULL,
|
||
(PVOID)Va,
|
||
0x4000,
|
||
&ThisSymbol,
|
||
NULL
|
||
);
|
||
if ( NT_SUCCESS(Status) ) {
|
||
if ( LastSymbol.Value && LastSymbol.Value == ThisSymbol.Value ) {
|
||
CountAtSymbol += *Counter;
|
||
}
|
||
else {
|
||
if ( LastSymbol.Value ) {
|
||
if ( CountAtSymbol ) {
|
||
n= sprintf(Line,"%d,%s,%S\n",
|
||
CountAtSymbol,
|
||
ProfileObject[i].ImageName,
|
||
&LastSymbol.Name
|
||
);
|
||
RtlpWriteProfileLine(ProfileHandle,Line,n);
|
||
}
|
||
}
|
||
CountAtSymbol = *Counter;
|
||
LastSymbol = ThisSymbol;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if ( CountAtSymbol ) {
|
||
n= sprintf(Line,"%d,%s,%S\n",
|
||
CountAtSymbol,
|
||
ProfileObject[i].ImageName,
|
||
&LastSymbol.Name
|
||
);
|
||
RtlpWriteProfileLine(ProfileHandle,Line,n);
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < NumberOfProfileObjects; i++) {
|
||
Buffer = ProfileObject[i].Buffer;
|
||
RtlZeroMemory(Buffer,ProfileObject[i].BufferSize);
|
||
}
|
||
NtClose(ProfileHandle);
|
||
return Status;
|
||
}
|