singrdk/base/Windows/singx86/diagnose.cpp

2964 lines
71 KiB
C++
Raw Normal View History

2008-11-17 18:29:00 -05:00
/////////////////////////////////////////////////////////////////////////////
//
// diagnose.cpp - Extension to find parse Singularity trace log.
//
// Copyright Microsoft Corporation. All rights reserved.
//
#include "singx86.h"
#include "diagnose.h"
ULONG pointerSize;
volatile BOOL BreakOnInternalError = FALSE;
extern bool DumpAllEvents;
extern bool DumpStackTraces;
extern bool DumpDescriptions;
bool DumpKernelOnly;
#include "..\..\Kernel\native\Csformat.inc"
//////////////////////////////////////////////////////////////////////////////
//
static HRESULT Usage()
{
ExtOut("Usage:\n"
" !diagnose {options}\n"
"Options:\n"
" Tracing commands:\n"
" [-s <SourcePatternFilter>] [-t <TypePatternFilter>] [-o <SortArgument>] [-f <Filter1>] [-f <Filter2>] [...]\n"
"\n"
" Filter format:\n"
" -f <Field_Name>[<=,<,>,!> <Field_Value>]\n"
" See below sample usages. If no value is specified, it will select all types\n"
" defining that field\n"
"\n"
" Sample commands:\n"
" !diagnose -n : dumps a sumary of basic types and sources available in the system\n"
" !diagnose -t *: Type filtering. The output will organize all entries by type\n"
" : all events of all types will be displayed\n"
" !diagnose -s *Tracing* -t *LOG*: Same as above, but only types which will have the string\n"
" : LOG anywhere in the name will be selected\n"
" !diagnose -t *LOG* -o Timestamp : Same as above, but the output will have the entries\n"
" : sorted by the column Timestamp. Any numeric column\n"
" : name is alowed for sorting. Strings not yet ...\n"
" !diagnose -t *LOG* -f Msg=\"*alarm*\" : Filter all entries of type containing LOG\n"
" : and arguments defined as Msg to contain alarm\n"
" : substring. The output can be combined with\n"
" : the sort option or other filtering options\n"
" !diagnose -t *LOG* -f Size>10 -f Size<40: Filter all entries of size between\n"
" : (10,40). Multiple conditions on the same field\n"
" : are allowed\n"
" Garbage collector commands:\n"
" -g <prefix> : Write all profile logs that have been collected in files\n"
" named based upon provided prefix\n"
" -ek <MemSize> [break] : Enable kernel GC profiling with a buffer of specified size in MBytes.\n"
" Optionally break into the debugger before recycling buffers\n"
" -es <MemSize> [break] : As above, but for SIP GC profiling rather than Kernel GC profiling\n"
" -dk : Disable the kernel GC profiling\n"
" -ds : Disable the SIP GC profiling. Applies only for new SIPs\n"
);
return S_FALSE;
}
//
// EVENTING AND TRACING SUPPORT
// To keep consistency with the Singularity structures
// the code leverages the same definitions present in the SystemEvents.inl file
// TODO: This can be improved further to take less dependencies with valid symbols for kernel
#define TYPE_UIntPtr ULONG64
#define TYPE_uint8 ULONG64
#define TYPE_uint16 ULONG64
#define TYPE_uint32 ULONG64
#define TYPE_uint64 ULONG64
#define TYPE_PCHAR ULONG64
#define TYPE_BOOL ULONG64
// Define the structure
#define DECLARE_STRUCTURE_BEGIN(x,d) struct S##x {
#define DECLARE_STRUCTURE_END(x) };
#define DECLARE_FIELD(c,t,n) t n;
#define DECLARE_SPECIAL_FIELD(c,t,n) ULONG64 n;
#define _CONSTANT_DEFINITIONS 1
#define DECLARE_EXTENDED_ARRAY_FIELD(c,t,n) ULONG64 n;
#define DECLARE_GENERIC_FIELD(s,t,sz,t1,n) ULONG64 n;
#include "..\..\Kernel\native\SystemEvents.inl"
// Declare the fields descriptors
#define DECLARE_STRUCTURE_BEGIN(x,d) FieldType _Fields##x[] = {
#define DECLARE_STRUCTURE_END(x) FIELDEND(), };
#define DECLARE_FIELD(c,t,n) { NULL, #n, offsetof(S##c,##n), 0, 0, 0, 0 },
#define DECLARE_GENERIC_FIELD(c,t,sz,t1,n) { NULL, #n, offsetof(S##c,##n), 0, 0, 0, 0 },
#define DECLARE_SPECIAL_FIELD DECLARE_FIELD
#define DECLARE_EXTENDED_ARRAY_FIELD DECLARE_FIELD
#include "..\..\Kernel\native\SystemEvents.inl"
// Instantiate the log object for each defined structure.
#define DECLARE_STRUCTURE_BEGIN(x,d) StructType Struct##x= StructType("_"#x,sizeof(S##x), _Fields##x);
#include "..\..\Kernel\native\SystemEvents.inl"
int gFieldTypeSizes[] = {0,1,1, 2, 2, 4, 4, 8, 8, 4, 4, 0};
char * gTypeNames[] = {
"unknown",
"int8",
"uint8",
"int16",
"uint16",
"int32",
"uint32",
"int64",
"uint64",
"IntPtr",
"UIntPtr",
"OutOfRangeType"};
// Utility functions to access the fields and types definitions
int GetPtrSize()
{
if (g_ExtControl->IsPointer64Bit() == S_OK) {
return 8;
}
return 4;
}
int GetFieldSize(int type)
{
if (type < sizeof(gFieldTypeSizes) / sizeof(gFieldTypeSizes[0])) {
if ((type == FIELD_TYPE__IntPtr) || (type == FIELD_TYPE__UIntPtr)) {
return GetPtrSize();
}
return gFieldTypeSizes[type];
}
if (type & FIELD_TYPE_VARIABLE_SIZE) {
return sizeof(USHORT);
}
return 0;
}
char * GetFieldTypeName(int type)
{
if (type < sizeof(gTypeNames) / sizeof(gTypeNames[0])) {
return gTypeNames[type];
}
if (type & FIELD_TYPE_VARIABLE_ANY_STRING) {
return "String";
}
if (type & FIELD_TYPE__arrayType) {
return "Array";
}
return "";
}
bool MatchPatternString(PatternMatch matchMode, char * p, char* pattern)
{
switch (matchMode) {
case exact:
return (_stricmp(p, pattern) == 0);
break;
case anywhere:
return (strstr(p, pattern) != NULL);
break;
case begin:
return (strstr(p, pattern) == p);
break;
}
return FALSE;
}
char * ReadToken(PCSTR& args)
{
char * arg = (char *)args;
char fn[256];
char *tmpChar = fn;
char *limit = fn + sizeof(fn) - 1;
SKIP_WHITESPACES(arg);
while (*arg && (strchr("=<>! ",*arg) == NULL) && (tmpChar < limit)) {
*tmpChar++ = *arg++;
}
*tmpChar = 0;
SKIP_WHITESPACES(arg);
args = arg;
return _strdup(fn);
}
// Local buffers and constants for extension
char EntryBuffer[4096];
ULONG_PTR HeaderSize = 0;
ULONG_PTR Stacksize = 0;
ULONG_PTR MetadataSize = 0;
ULONG64 ReadSize;
EventTypeEntry * SystemHeaderType = NULL;
EventTypeEntry * SystemTypeDescriptor = NULL;
UINT64 GetStackTrace(int index) {
if (Stacksize == 0) return 0;
if (pointerSize == sizeof(ULONG)) {
int pos = index * sizeof(ULONG);
if ((pos + sizeof(ULONG)) > Stacksize) return 0;
ULONG * ptr = (ULONG * )(EntryBuffer + StructMEMORY_HEADER.size + pos);
return *ptr;
} else {
int pos = index * sizeof(ULONG64);
if ((pos + sizeof(ULONG64)) > Stacksize) return 0;
ULONG64 * ptr = (ULONG64 * )(EntryBuffer + StructMEMORY_HEADER.size + pos);
return *ptr;
}
return 0;
}
//
// VariableArray class implementation
//
VariableArray::VariableArray(){
Size = 1024;
Array = (UINT64 *)malloc(Size * sizeof(UINT64));
InUse = 0;
}
VariableArray::~VariableArray()
{
if (Array != NULL) {
free(Array);
}
}
bool VariableArray::Extend()
{
int newSize = Size * 2;
if (newSize > 1024*1024) {
newSize = Size + 1024*1024;
}
UINT64 *newArray = (UINT64 *)malloc(newSize * sizeof(UINT64));
if (newArray == NULL) {
return FALSE;
}
memcpy(newArray, Array, sizeof(UINT64) * Size);
Size = newSize;
free(Array);
Array = newArray;
return TRUE;
}
bool VariableArray::Add(UINT64 value) {
if (InUse >= Size) {
if (!Extend()) return FALSE;
}
Array[InUse++] = value;
return TRUE;
}
//
// Symbolic enum and flag support
//
EnumType::~EnumType()
{
while (ValuesLists) {
SymbolicValue * crt = ValuesLists;
ValuesLists = ValuesLists->Next;
delete crt;
}
if (Name) free (Name);
}
void EnumType::AddSymbolicValue(SymbolicValue * symValue)
{
if (symValue->FlagChar) {
NumFlags += 1;
FlagsMask += symValue->Value;
} else {
int len = (int)strlen(symValue->Name);
if (PrintWidth < len) {
PrintWidth = len;
}
}
symValue->Next = ValuesLists;
ValuesLists = symValue;
}
int EnumType::GetFieldPrintWidth()
{
if (FlagsMask) {
return NumFlags + 2 + PrintWidth;
} else {
return PrintWidth;
}
}
ULONG64 EnumType::GetFieldNumericValue(int offset) {
switch (BasicType) {
case FIELD_TYPE__int8:
case FIELD_TYPE__uint8:
return *((BYTE *)(EntryBuffer + MetadataSize + offset));
case FIELD_TYPE__int16:
case FIELD_TYPE__uint16:
return *((USHORT *)(EntryBuffer + MetadataSize + offset));
case FIELD_TYPE__int32:
case FIELD_TYPE__uint32:
return *((UINT *)(EntryBuffer + MetadataSize + offset));
case FIELD_TYPE__int64:
case FIELD_TYPE__uint64:
return *((UINT64 *)(EntryBuffer + MetadataSize + offset));
case FIELD_TYPE__IntPtr:
case FIELD_TYPE__UIntPtr:
return *((UINT_PTR *)(EntryBuffer + MetadataSize + offset));
}
// Non-numeric values return 0
return 0;
}
int EnumType::PrintValue(int printWidth, int offset)
{
ULONG64 value;
value = GetFieldNumericValue(offset);
ExtOut(" ");
if (FlagsMask) {
for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {
if (crt->Value & value) {
ExtOut("%c",crt->FlagChar);
} else {
ExtOut(".",crt->FlagChar);
}
}
}
if (PrintWidth > 0) {
value &= ~FlagsMask;
for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {
if (crt->Value == value) {
char buff[20];
_snprintf(buff, sizeof(buff), "%%%lds", printWidth);
ExtOut(buff, crt->Name);
}
}
}
return 0;
}
PCHAR EnumType::GetStringSymbol(int offset)
{
ULONG64 value;
value = GetFieldNumericValue(offset);
if (FlagsMask) {
CHAR buffer[65];
int crtPos = 0;
for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {
if (crt->Value & value) {
buffer[crtPos++] = crt->FlagChar;
} else {
buffer[crtPos++] = '.';
}
}
buffer[crtPos] = 0;
return _strdup(buffer);
}
if (PrintWidth > 0) {
value &= ~FlagsMask;
for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {
if (crt->Value == value) {
return _strdup(crt->Name);
}
}
}
return _strdup("???");
}
//
// FieldEntry class implementation
//
FieldEntry::FieldEntry()
{
Name=NULL;
PrintWidth = 0;
SymType = NULL;
}
FieldEntry::~FieldEntry()
{
if (Name) {
free(Name);
}
}
int FieldEntry::GetArraySize(void * buffer)
{
if ((Type & FIELD_TYPE_VARIABLE_SIZE) == 0) {
// Not a variable size array. return size 0
return 0;
}
USHORT length = *(USHORT *)buffer;
int basicType = Type & ~FIELD_TYPE_VARIABLE_SIZE;
return length / GetFieldSize(basicType);
}
ULONG64 FieldEntry::GetFieldNumericValue(void * buffer, int index) {
if ((Type & FIELD_TYPE_VARIABLE_SIZE) == 0) {
// Not a variable size array. return size 0
return 0;
}
USHORT length = *(USHORT *)buffer;
int basicType = Type & ~FIELD_TYPE_VARIABLE_SIZE;
int count = length / GetFieldSize(basicType);
if ((index < 0) || (index >= count)) {
return 0;
}
buffer = (UCHAR*)buffer + sizeof(USHORT) + index * GetFieldSize(basicType);
switch (basicType) {
case FIELD_TYPE__int8:
case FIELD_TYPE__uint8:
return *((BYTE *)(buffer));
case FIELD_TYPE__int16:
case FIELD_TYPE__uint16:
return *((USHORT *)(buffer));
case FIELD_TYPE__int32:
case FIELD_TYPE__uint32:
return *((UINT *)(buffer));
case FIELD_TYPE__int64:
case FIELD_TYPE__uint64:
return *((UINT64 *)(buffer));
case FIELD_TYPE__IntPtr:
case FIELD_TYPE__UIntPtr:
return *((UINT_PTR *)(buffer));
}
// Non-numeric values return 0
return 0;
}
ULONG64 FieldEntry::GetFieldNumericValue() {
if ((Type != FIELD_TYPE_GENERIC_TYPE) && (Type & FIELD_TYPE_VARIABLE_ANY_STRING)) {
return *((USHORT *)(EntryBuffer + MetadataSize + Offset));
}
switch (Type) {
case FIELD_TYPE__int8:
case FIELD_TYPE__uint8:
return *((BYTE *)(EntryBuffer + MetadataSize + Offset));
case FIELD_TYPE__int16:
case FIELD_TYPE__uint16:
return *((USHORT *)(EntryBuffer + MetadataSize + Offset));
case FIELD_TYPE__int32:
case FIELD_TYPE__uint32:
return *((UINT *)(EntryBuffer + MetadataSize + Offset));
case FIELD_TYPE__int64:
case FIELD_TYPE__uint64:
return *((UINT64 *)(EntryBuffer + MetadataSize + Offset));
case FIELD_TYPE__IntPtr:
case FIELD_TYPE__UIntPtr:
return *((UINT_PTR *)(EntryBuffer + MetadataSize + Offset));
case FIELD_TYPE_GENERIC_TYPE:
{
if (SymType == NULL) {
SymType = Controller->FindEnum(TypeKey);
EventTypeEntry * desc = Controller->FindType(ParentKey);
if (desc) {
desc->UpdateFieldsOffsets();
}
}
if (SymType != NULL) {
return SymType->GetFieldNumericValue(Offset);
}
}
break;
}
// Non-numeric values return 0
return 0;
}
ULONG64 FieldEntry::PrintValue(EventTypeEntry * type)
{
if (PrintWidth == 0) {
GetFieldPrintWidth();
}
if (Type == FIELD_TYPE_GENERIC_TYPE) {
if (SymType == NULL) {
SymType = Controller->FindEnum(TypeKey);
EventTypeEntry * desc = Controller->FindType(ParentKey);
if (desc) {
desc->UpdateFieldsOffsets();
}
}
if (SymType != NULL) {
SymType->PrintValue(PrintWidth, Offset);
}
return 0;
}
if (Type & FIELD_TYPE_VARIABLE_ANY_STRING) {
int index = (int)GetFieldNumericValue();
char * str = type->GetExtendedString(index);
if (str) ExtOut(" \"%s\" ",str);
return 0;
}
char format[20];
switch (Type) {
case FIELD_TYPE__int8:
case FIELD_TYPE__uint8:
_snprintf(format, sizeof(format), "%%%ldx", PrintWidth);
ExtOut(format,(int)(*((byte *)(EntryBuffer + MetadataSize + Offset))));
break;
case FIELD_TYPE__int16:
case FIELD_TYPE__uint16:
_snprintf(format, sizeof(format), "%%%ldx", PrintWidth);
ExtOut(format,(int)*((USHORT *)(EntryBuffer + MetadataSize + Offset)));
break;
case FIELD_TYPE__int32:
case FIELD_TYPE__uint32:
_snprintf(format, sizeof(format), "%%%ldx", PrintWidth);
ExtOut(format, *((int *)(EntryBuffer + MetadataSize + Offset)));
break;
case FIELD_TYPE__IntPtr:
case FIELD_TYPE__UIntPtr:
ExtOut(" %p",*((UINT_PTR *)(EntryBuffer + MetadataSize + Offset)));
break;
case FIELD_TYPE__int64:
case FIELD_TYPE__uint64:
_snprintf(format, sizeof(format), "%%%ldI64x", PrintWidth);
ExtOut(format, *((UINT64 *)(EntryBuffer + MetadataSize + Offset)));
break;
}
// Non-numeric values return 0
return 0;
}
int FieldEntry::GetFieldPrintWidth()
{
if ((Type != FIELD_TYPE_GENERIC_TYPE) && (Type & FIELD_TYPE_VARIABLE_ANY_STRING)) {
return 30;
}
if (PrintWidth > 0) return PrintWidth;
switch (Type) {
case FIELD_TYPE__int8:
case FIELD_TYPE__uint8:
PrintWidth = 4;
break;
case FIELD_TYPE__int16:
case FIELD_TYPE__uint16:
PrintWidth = 6;
break;
case FIELD_TYPE__int32:
case FIELD_TYPE__uint32:
PrintWidth = 10;
break;
case FIELD_TYPE__int64:
case FIELD_TYPE__uint64:
PrintWidth = 18;
break;
case FIELD_TYPE__IntPtr:
case FIELD_TYPE__UIntPtr:
PrintWidth = GetPtrSize() + 6;
break;
case FIELD_TYPE_GENERIC_TYPE:
{
if (SymType == NULL) {
SymType = Controller->FindEnum(TypeKey);
EventTypeEntry * desc = Controller->FindType(ParentKey);
if (desc) {
desc->UpdateFieldsOffsets();
}
}
if (SymType != NULL) {
PrintWidth = SymType->GetFieldPrintWidth() + 2;
}
}
break;
}
if ((PrintWidth > 0) && ((strlen(Name) + 1) > PrintWidth)) {
PrintWidth = (unsigned int)strlen(Name) + 1;
}
return PrintWidth;
}
//
// StringPattern class implementation
//
StringPattern::StringPattern()
{
Pattern = NULL;
}
StringPattern::~StringPattern()
{
Cleanup();
}
void StringPattern::Cleanup()
{
if (Pattern) {
free(Pattern);
}
Pattern = NULL;
}
bool StringPattern::IsMatch(char * p)
{
return MatchPatternString(MatchMode, p, Pattern);
}
char * StringPattern::ParseInput(char * arg)
{
char fn[256];
char *tmpChar = fn;
char *limit = fn + sizeof(fn) - 1;
char endChar = ' ';
Cleanup();
SKIP_WHITESPACES(arg);
MatchMode = exact;
if (*arg == '\"') {
endChar = '\"';
arg++;
}
if (*arg == '*') {
MatchMode = anywhere;
arg++;
}
while (*arg && (*arg != endChar) && (tmpChar < limit)) {
if (*arg != '*') {
*tmpChar++ = *arg++;
} else {
arg++;
if (MatchMode == exact) {
MatchMode = begin;
}
}
}
*tmpChar = 0;
Pattern = _strdup(fn);
if (*arg == '\"') {
arg++;
}
SKIP_WHITESPACES(arg);
return arg;
}
//
// FieldFilter class implementation
//
FieldFilter::FieldFilter()
{
FieldNamePattern = NULL;
StringValuePattern = NULL;
}
FieldFilter::~FieldFilter()
{
Cleanup();
}
void FieldFilter::Cleanup()
{
if (FieldNamePattern) free(FieldNamePattern);
if (StringValuePattern) free(StringValuePattern);
FieldNamePattern= NULL;
StringValuePattern = NULL;
NumericValuePattern = 0;
}
FieldFilter::FieldFilter(PatternMatch matchMode,
char * fieldPattern,
FilterOperation op,
UINT64 value,
char * strValue)
{
FieldMatchMode = matchMode;
Operation = op;
FieldNamePattern = fieldPattern;
NumericValuePattern = value;
StringValuePattern = strValue;
}
bool FieldFilter::MatchFieldName(char * p)
{
return MatchPatternString(FieldMatchMode, p, FieldNamePattern);
}
bool FieldFilter::MatchFieldValue(INT64 value)
{
switch (Operation) {
case all:
return TRUE;
case equal:
return (NumericValuePattern == value);
case less:
return (NumericValuePattern > value);
case greater:
return (NumericValuePattern < value);
case different:
return (value != NumericValuePattern);
}
return FALSE;
}
bool FieldFilter::MatchFieldValue(char * value)
{
switch (Operation){
case all:
return TRUE;
case equal:
return (StringValuePattern != NULL) && (_stricmp(value, StringValuePattern) == 0);
case less:
return (StringValuePattern != NULL) && (_stricmp(value, StringValuePattern) < 0);
case greater:
return (StringValuePattern != NULL) && (_stricmp(value, StringValuePattern) > 0);
case contains:
return (StringValuePattern != NULL) && (strstr(value, StringValuePattern) != NULL);
case different:
return (StringValuePattern != NULL) && (strstr(value, StringValuePattern) == NULL);
}
return FALSE;
}
char * FieldFilter::ParseInput(char * arg)
{
char fn[256];
char *tmpChar = fn;
char *limit = fn + sizeof(fn) - 1;
Cleanup();
SKIP_WHITESPACES(arg);
FieldMatchMode = exact;
Operation = all;
if (*arg == '*') {
FieldMatchMode = anywhere;
arg++;
}
while (*arg && (strchr("=<>!",*arg) == NULL) && (tmpChar < limit)) {
if (*arg != '*') {
*tmpChar++ = *arg++;
} else {
arg++;
if (FieldMatchMode == exact) {
FieldMatchMode = begin;
}
}
}
*tmpChar = 0;
FieldNamePattern = _strdup(fn);
SKIP_WHITESPACES(arg);
if (*arg == 0) {
return arg;
}
if (strchr("=<>!",*arg)) {
switch (*arg) {
case '=' :
Operation = equal;
break;
case '<' :
Operation = less;
break;
case '>' :
Operation = greater;
break;
case '!' :
Operation = different;
break;
}
arg++;
if (*arg == '\"') {
arg++;
if (*arg == '*') {
Operation = contains;
arg++;
}
tmpChar = fn;
while (*arg && (*arg != '\"') && (tmpChar < limit)) {
if (*arg != '*') {
*tmpChar++ = *arg++;
} else {
arg++;
Operation = contains;
}
}
*tmpChar = 0;
if (*arg) arg++;
StringValuePattern = _strdup(fn);
} else if (*arg) {
PCSTR tmpChar = (PCSTR)arg;
NumericValuePattern = GetValue(tmpChar, true);
arg = (char*)tmpChar;
}
}
SKIP_WHITESPACES(arg);
return arg;
}
// EventTypeEntry class implementation
EventTypeEntry::EventTypeEntry()
{
Name=NULL;
Descriptor = NULL;
size = 0;
Key = 0;
NumFields = 0;
filterCount = 0;
FilterApplied = FALSE;
}
EventTypeEntry::~EventTypeEntry() {
for (int i = 0; i < NumFields; i++) {
delete Fields[i];
}
if (Name) {
free(Name);
}
if (Descriptor) {
free(Descriptor);
}
}
bool EventTypeEntry::TestFilter(FieldFilter * newFilter)
{
FilterApplied = TRUE;
if (filterCount >= MAX_FILTERS) {
return false;
}
// Test whether any of the fields inside this type entry would match
// at least the fieldNamePattern;
for (int i = 0; i < NumFields; i++) {
FieldEntry * field = Fields[i];
if (newFilter->MatchFieldName(field->Name)) {
return TRUE;
}
}
return FALSE;
}
FieldEntry * EventTypeEntry::GetField(PCHAR name)
{
for (int i = 0; i < NumFields; i++) {
FieldEntry * field = Fields[i];
if (_stricmp(field->Name, name) == 0) {
return field;
}
}
return NULL;
}
int EventTypeEntry::GetFieldIndex(PCHAR name)
{
for (int i = 0; i < NumFields; i++) {
FieldEntry * field = Fields[i];
if (_stricmp(field->Name, name) == 0) {
return i;
}
}
return -1;
}
bool EventTypeEntry::ApplyFilter(FieldFilter * newFilter)
{
FilterApplied = TRUE;
if (filterCount >= MAX_FILTERS) {
return false;
}
// Test whether any of the fields inside this type entry would match
// at least the fieldNamePattern;
for (int i = 0; i < NumFields; i++) {
FieldEntry * field = Fields[i];
if (newFilter->MatchFieldName(field->Name)) {
fieldsFilterList[filterCount] = field;
filterList[filterCount++] = newFilter;
return TRUE;
}
}
return FALSE;
}
void EventTypeEntry::ClearFilters()
{
filterCount = 0;
}
void EventTypeEntry::AddNewField(FieldEntry * field) {
if (NumFields < MAX_FIELDS) {
Fields[NumFields++] = field;
}
}
void EventTypeEntry::UpdateFieldsOffsets() {
int offset = 0;
ExtendedFieldsCount = 0;
for (int i = 0; i < NumFields; i++) {
FieldEntry * field = Fields[i];
field->ExtendedFieldIndex = 0;
if (field->Type == FIELD_TYPE_GENERIC_TYPE) {
if (field->SymType != NULL) {
if (field->Offset == 0) {
field->Offset = offset;
}
field->Size = GetFieldSize(field->SymType->BasicType);
offset = field->Offset + GetFieldSize(field->SymType->BasicType);
}
} else {
if (field->Offset == 0) {
field->Offset = offset;
}
field->Size = GetFieldSize(field->Type);
offset = field->Offset + GetFieldSize(field->Type);
}
if (field->Type & FIELD_TYPE_VARIABLE_SIZE) {
field->ExtendedFieldIndex = ExtendedFieldsCount++;
}
}
size = offset;
}
ULONG_PTR SWITCH_TO_METADATA_CURSOR()
{
ULONG_PTR savedMetadataSize = MetadataSize;
MetadataSize = 0;
return savedMetadataSize;
}
void RESTORE_CURSOR(ULONG_PTR savedCursor)
{
MetadataSize = savedCursor;
}
bool EventTypeEntry::FilterMatch()
{
if ((SystemHeaderType != NULL) && (SystemHeaderType != this)) {
ULONG_PTR savedMetadataSize = MetadataSize;
MetadataSize = 0;
bool success = SystemHeaderType->FilterMatch();
MetadataSize = savedMetadataSize;
if (success && (filterCount == 0)) {
return TRUE;
}
}
if (FilterApplied && (filterCount == 0)) {
return FALSE;
}
for (int i = 0; i < filterCount; i++) {
FieldEntry * field = fieldsFilterList[i];
if (field->Type & FIELD_TYPE_VARIABLE_ANY_STRING) {
int index = (int)field->GetFieldNumericValue();
char * str = NULL;
if (index) {
str = GetExtendedString(index);
}
if (!filterList[i]->MatchFieldValue(str)) {
return FALSE;
}
} else {
if (!filterList[i]->MatchFieldValue(field->GetFieldNumericValue())) {
return FALSE;
}
}
}
return TRUE;
}
static int argIndexOffset = 0;
int PrintEventField(void * context, char *pszOut, int bufferSize, int aln, int wid, char fmt, int argIdx)
{
EventTypeEntry * typeEntry = (EventTypeEntry *) context;
argIdx += argIndexOffset;
if (argIdx >= typeEntry->NumFields) {
return 0;
}
FieldEntry * field = typeEntry->Fields[argIdx];
int retValue = 0;
char * str = NULL;
if (field->Type == FIELD_TYPE_GENERIC_TYPE) {
// Force fetching the symtype if needed
field->GetFieldNumericValue();
PCHAR str = field->SymType->GetStringSymbol(field->Offset);
if (str != NULL) {
if (wid > 0) {
if (aln < wid) {
aln = wid;
}
retValue = _snprintf(pszOut, bufferSize, "%*.*s", aln, wid, str);
}
else {
retValue = _snprintf(pszOut, bufferSize, "%*s", aln, str);
}
free(str);
}
} else if (field->Type & FIELD_TYPE_VARIABLE_ANY_STRING) {
int index = (int)field->GetFieldNumericValue();
char * str = typeEntry->GetExtendedString(index);
if (wid > 0) {
if (aln < wid) {
aln = wid;
}
retValue = _snprintf(pszOut, bufferSize, "%*.*s", aln, wid, str);
}
else {
retValue = _snprintf(pszOut, bufferSize, "%*s", aln, str);
}
} else {
if (aln < wid) {
aln = wid;
}
if (fmt == 'x') {
if (wid > 0) {
retValue = _snprintf(pszOut, bufferSize, "%0*x", aln,
field->GetFieldNumericValue());
}
else {
retValue = _snprintf(pszOut, bufferSize, "%*x", aln,
field->GetFieldNumericValue());
}
}
else {
retValue = _snprintf(pszOut, bufferSize, "%*d", aln,
field->GetFieldNumericValue());
}
}
return retValue;
}
void EventTypeEntry::Format(int formatStringIndex, int argOffset)
{
CHAR msg[512];
argIndexOffset = argOffset + 1;
char * text = GetExtendedString(formatStringIndex);
FormatCSOutput(this, text, msg, sizeof(msg),PrintEventField, NULL);
ExtOut("%s", msg);
}
void EventTypeEntry::PrintDescription()
{
if (Descriptor == NULL) return;
CHAR msg[512];
argIndexOffset = 0;
FormatCSOutput(this, Descriptor, msg, sizeof(msg),PrintEventField, NULL);
ExtOut("%s", msg);
}
void EventTypeEntry::WalkFields(EventingEnumerator * enumerator) {
if ((SystemHeaderType != NULL) && (SystemHeaderType != this) && (MetadataSize != 0)) {
ULONG_PTR savedMetadataSize = MetadataSize;
MetadataSize = 0;
SystemHeaderType->WalkFields(enumerator);
MetadataSize = savedMetadataSize;
}
for (int i = 0; i < NumFields; i++) {
enumerator->FieldCallout(this, Fields[i]);
}
}
char * GetExtendedStringUnsafe(int offset, int index)
{
offset = ROUND_UP_TO_POWER2(offset, pointerSize);
for (int i = 1; i < index; i++) {
USHORT length;
// copy the structure localy since it may not be aligned.
memcpy(&length, EntryBuffer + offset, sizeof(USHORT));
offset += length + sizeof(USHORT);
}
char * ptr = EntryBuffer + offset + sizeof(USHORT); // skip also the size of the string
return ptr;
}
char * EventTypeEntry::GetExtendedString(int index)
{
if ((index > 0) && (index <= ExtendedFieldsCount)) {
int offset = (int)MetadataSize + size;
offset = ROUND_UP_TO_POWER2(offset, pointerSize);
for (int i = 1; i < index; i++) {
USHORT length;
// copy the structure localy since it may not be aligned.
memcpy(&length, EntryBuffer + offset, sizeof(USHORT));
offset += length + sizeof(USHORT);
}
char * ptr = EntryBuffer + offset + sizeof(USHORT); // skip also the size of the string
return ptr;
}
return NULL;
}
void * EventTypeEntry::GetFieldArray(int index)
{
if ((index >= 0) && (index <= ExtendedFieldsCount)) {
int offset = (int)MetadataSize + size;
offset = ROUND_UP_TO_POWER2(offset, pointerSize);
for (int i = 1; i < index; i++) {
USHORT length;
// copy the structure localy since it may not be aligned.
memcpy(&length, EntryBuffer + offset, sizeof(USHORT));
offset += length;
}
return (char *)EntryBuffer + offset;
}
return NULL;
}
void EventTypeEntry::EnumerateEntries(EventingEnumerator * enumerator, UINT64 address, ULONG count) {
ULONG readSize = size;
if (readSize > sizeof(EntryBuffer)) {
readSize = sizeof(EntryBuffer);
}
for (ULONG j = 0; j < count; j++) {
TraceRead(address, EntryBuffer, (ULONG)readSize);
MetadataSize = 0;
if (enumerator->ActiveEntryCallout(this, FALSE)) {
for (int i = 0; i < NumFields; i++) {
enumerator->FieldCallout(this, Fields[i]);
}
enumerator->ActiveEntryCallout(this, TRUE);
}
address += size;
}
}
//
// SourceEntry class implementation
//
SourceEntry::SourceEntry()
{
Name=NULL;
}
SourceEntry::~SourceEntry()
{
if (Name) {
free(Name);
}
}
void SourceEntry::EnumerateEntries(EventingEnumerator * enumerator, ControllerObject *controller) {
if (StorageHandle == 0) {
if (enumerator->ActiveSourceCallout(this, FALSE, controller)) {
EventTypeEntry * typeEntry = controller->FindType(EventTypeHandle);
if (typeEntry != NULL) {
typeEntry->EnumerateEntries(enumerator,
DebuggerBufferAddress,
(ULONG)Count);
}
enumerator->ActiveSourceCallout(this, TRUE, controller);
}
}
}
//
// EventingEnumerator class implementation
//
EventingEnumerator::EventingEnumerator()
{
FiltersCount = 0;
Filters = NULL;
SourcePattern = NULL;
usedStorages = 0;
}
bool EventingEnumerator::AddStorageFilter(ULONG64 storageHandle)
{
if (usedStorages >= MAX_STORAGE_FILTERS) return false;
StorageHandles[usedStorages++] = storageHandle;
return true;
}
bool EventingEnumerator::MatchStorageHandle(ULONG64 storageHandle)
{
if (SourcePattern == NULL) return true;
for (int i = 0; i < usedStorages; i++) {
if (StorageHandles[i] == storageHandle) return true;
}
return false;
}
bool EventingEnumerator::ApplyFilters(FieldFilter ** filters, int Count) {
FiltersCount = Count;
Filters = filters;
return TRUE;
}
bool EventingEnumerator::ApplySourceSelector(StringPattern * sourcePattern) {
SourcePattern = sourcePattern;
return TRUE;
}
bool EventingEnumerator::TypeCallout(EventTypeEntry * entryDescriptor){
for (int i = 0; i < FiltersCount; i++) {
if (!entryDescriptor->ApplyFilter(Filters[i])) {
// This filter does not apply for the basic type
// test it against the header
if (!SystemHeaderType->TestFilter(Filters[i])) {
return FALSE;
}
}
}
return TRUE;
}
bool EventingEnumerator::MedataCallout(ControllerObject *controller)
{
if (SourcePattern) {
ClearStorageFilters();
for (int i = 0; i < controller->SourcesCount; i++) {
SourceEntry *src = controller->RegisteredSources[i];
if (SourcePattern->IsMatch(src->Name)) {
AddStorageFilter(src->StorageHandle);
}
}
}
return TRUE;
}
bool EventingEnumerator::StorageCallout(UINT64 storageAddress, bool finished)
{
if ((SourcePattern == NULL) || MatchStorageHandle(storageAddress)) return TRUE;
return FALSE;
}
//
// Controller objects managements
//
ControllerObject * Controllers[MAX_CONTROLLERS];
int ControllersCount = 0;
ControllerObject * AllocateController(UINT64 handle,
UINT64 contextHandle,
UINT64 storageAddress,
UINT64 storageListHead)
{
if (ControllersCount >= MAX_CONTROLLERS) {
return NULL;
}
ControllerObject * ctrl = new ControllerObject(handle,
contextHandle,
storageAddress,
storageListHead);
Controllers[ControllersCount++] = ctrl;
return ctrl;
}
void FlushControllers()
{
for (int i = 0; i < ControllersCount; i++) {
delete Controllers[i];
Controllers[i] = NULL;
}
ControllersCount = 0;
}
ControllerObject::ControllerObject(UINT64 handle,
UINT64 contextHandle,
UINT64 storageAddress,
UINT64 storageListHead)
{
TypesCount = 0;
EnumCount = 0;
SourcesCount = 0;
ControllerHandle = handle;
RepositoryAddress = storageAddress;
StorageListHead = storageListHead;
ContextHandle = contextHandle;
for (int i = 0; i < MAX_FIELDS; i++) {
RegisteredFieldsTypes[i] = NULL;
RegisteredSymbolicValues[i] = NULL;
}
}
ControllerObject::~ControllerObject() {
FlushTypeArray();
FlushSourceArray();
}
void ControllerObject::AddTempField(FieldEntry * field)
{
for (int i = 0; i < MAX_FIELDS; i++) {
if (RegisteredFieldsTypes[i] == NULL) {
RegisteredFieldsTypes[i] = field;
return;
}
}
ERRORBREAK("Registering field failed\n");
}
void ControllerObject::AddTempField(SymbolicValue * field)
{
for (int i = 0; i < MAX_FIELDS; i++) {
if (RegisteredSymbolicValues[i] == NULL) {
RegisteredSymbolicValues[i] = field;
return;
}
}
ERRORBREAK("Registering field failed\n");
}
void ControllerObject::PopulateFields(EnumType * type)
{
for (int i = MAX_FIELDS - 1; i >= 0 ; i--) {
if ((RegisteredSymbolicValues[i] != NULL) &&
(RegisteredSymbolicValues[i]->ParentKey == type->Key)) {
type->AddSymbolicValue(RegisteredSymbolicValues[i]);
RegisteredSymbolicValues[i] = NULL;
}
}
}
void ControllerObject::PopulateFields(EventTypeEntry * type)
{
for (int i = MAX_FIELDS - 1; i >= 0 ; i--) {
if ((RegisteredFieldsTypes[i] != NULL) &&
(RegisteredFieldsTypes[i]->ParentKey == type->Key)) {
type->AddNewField(RegisteredFieldsTypes[i]);
RegisteredFieldsTypes[i] = NULL;
}
}
type->UpdateFieldsOffsets();
}
EventTypeEntry * ControllerObject::FindType(ULONG64 Key)
{
for (int i = 0; i < TypesCount; i++) {
if (RegisteredTypes[i]->Key == Key) {
return RegisteredTypes[i];
}
}
return NULL;
}
void ControllerObject::AddewType(EventTypeEntry * newType)
{
if (TypesCount < MAX_TYPES) {
RegisteredTypes[TypesCount++] = newType;
}
}
EnumType * ControllerObject::FindEnum(ULONG64 Key)
{
for (int i = 0; i < EnumCount; i++) {
if (RegisteredEnums[i]->Key == Key) {
return RegisteredEnums[i];
}
}
return NULL;
}
void ControllerObject::AddNewEnum(EnumType * newEnum)
{
if (EnumCount < MAX_TYPES) {
RegisteredEnums[EnumCount++] = newEnum;
}
}
void ControllerObject::FlushTypeArray()
{
for (int i = 0; i < TypesCount; i++) {
delete RegisteredTypes[i];
}
TypesCount = 0;
for (int i = 0; i < EnumCount; i++) {
delete RegisteredEnums[i];
}
EnumCount = 0;
for (int i = 0; i < MAX_FIELDS; i++) {
RegisteredFieldsTypes[i] = NULL;
}
SystemHeaderType = NULL;
}
SourceEntry * ControllerObject::FindSource(ULONG64 Key)
{
for (int i = 0; i < SourcesCount; i++) {
if (RegisteredSources[i]->Key == Key) {
return RegisteredSources[i];
}
}
return NULL;
}
char * ControllerObject::GetControllerName()
{
#define CONTROLLER_NAME_PREFIX "ControllerLog{"
for (int i = 0; i < SourcesCount; i++) {
if (strstr(RegisteredSources[i]->Name, "ControllerLog{") == RegisteredSources[i]->Name) {
char * crt = RegisteredSources[i]->Name + strlen("ControllerLog{");
char tmpname[128];
char * cursor = tmpname;
char * end = cursor + sizeof(tmpname) - 1;
while (*crt && (cursor < end)) {
if (isalnum(*crt)) {
*cursor = *crt;
} else {
if (*crt == '}') {
if (char * pos = strchr(crt, ':')) {
crt = pos;
} else {
break;
}
}
*cursor = '_';
}
crt++;
cursor++;
}
*cursor = 0;
return _strdup(tmpname);
}
}
return NULL;
}
void ControllerObject::AddNewSource(SourceEntry * newSource)
{
if (SourcesCount < MAX_SOURCES) {
RegisteredSources[SourcesCount++] = newSource;
}
}
void ControllerObject::FlushSourceArray()
{
for (int i = 0; i < SourcesCount; i++) {
delete RegisteredSources[i];
}
SourcesCount = 0;
}
void ControllerObject::WalkTypes(EventingEnumerator * enumerator)
{
for (int i = 0; i < TypesCount; i++) {
enumerator->TypeCallout(RegisteredTypes[i]);
}
}
void ControllerObject::WalkSources(EventingEnumerator * enumerator)
{
for (int i = 0; i < SourcesCount; i++) {
enumerator->SourcesCallout(RegisteredSources[i]);
}
}
void ControllerObject::WalkActiveSourcesEntries(EventingEnumerator * enumerator)
{
for (int i = 0; i < SourcesCount; i++) {
RegisteredSources[i]->EnumerateEntries(enumerator, this);
}
}
void ControllerObject::ReadSource(UINT64 entryAddress)
{
HRESULT status;
SSOURCE_DESCRIPTOR log;
EXT_CHECK(StructSOURCE_DESCRIPTOR.Read(entryAddress, &log));
SourceEntry * sourceEntry;
sourceEntry = new SourceEntry();
if (sourceEntry != NULL) {
sourceEntry->Name = _strdup(EntryBuffer + StructMEMORY_HEADER.size + StructSOURCE_DESCRIPTOR.size);
sourceEntry->Key = entryAddress - StructMEMORY_HEADER.size;
sourceEntry->StorageHandle = log.StorageHandle;
sourceEntry->ControlFlags = (ULONG)log.ControlFlags;
sourceEntry->EventTypeHandle = log.EventTypeHandle;
sourceEntry->DebuggerBufferAddress = log.DebuggerBufferAddress;
sourceEntry->Count = log.Count;
sourceEntry->EntrySize = log.EntrySize;
AddNewSource(sourceEntry);
}
return ;
Exit:
// Error path
ERRORBREAK("Invalid symbols information for reading source structures\n");
return ;
}
void ControllerObject::ReadEventDescriptor(UINT64 entryAddress)
{
HRESULT status;
SEVENT_DESCRIPTOR log;
EXT_CHECK(StructEVENT_DESCRIPTOR.Read(entryAddress, &log));
EventTypeEntry * TypeEntry;
TypeEntry = new EventTypeEntry();
if (TypeEntry != NULL) {
TypeEntry->Key = entryAddress - StructMEMORY_HEADER.size;
TypeEntry->NumFields = 0;
TypeEntry->size = StructEVENT_DESCRIPTOR.size;
TypeEntry->ExtendedFieldsCount = 2;
TypeEntry->Name = _strdup(TypeEntry->GetExtendedString((int)log.Name));
if (log.Description > 0) {
TypeEntry->Descriptor = _strdup(TypeEntry->GetExtendedString((int)log.Description));
}
if (SystemHeaderType == NULL) {
if (_stricmp(TypeEntry->Name, "System.MEMORY_HEADER") == 0) {
SystemHeaderType = TypeEntry;
}
}
AddewType(TypeEntry);
PopulateFields(TypeEntry);
}
return ;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return ;
}
void ControllerObject::ReadEnumDescriptor(UINT64 entryAddress)
{
HRESULT status;
SENUM_DESCRIPTOR log;
EXT_CHECK(StructENUM_DESCRIPTOR.Read(entryAddress, &log));
EnumType * TypeEntry;
TypeEntry = new EnumType();
if (TypeEntry != NULL) {
TypeEntry->Key = entryAddress - StructMEMORY_HEADER.size;
TypeEntry->BasicType = (int)log.Type;
TypeEntry->Name = _strdup(GetExtendedStringUnsafe(
StructMEMORY_HEADER.size + StructENUM_DESCRIPTOR.size,
(int)log.Name));
AddNewEnum(TypeEntry);
PopulateFields(TypeEntry);
}
return ;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return ;
}
void ControllerObject::ReadValueDescriptor(UINT64 entryAddress, UINT64 parent)
{
HRESULT status;
SEVENT_VALUE_DESCRIPTOR log;
EXT_CHECK(StructEVENT_VALUE_DESCRIPTOR.Read(entryAddress, &log));
SymbolicValue * fieldEntry;
fieldEntry = new SymbolicValue;
if (fieldEntry != NULL) {
fieldEntry->Name = _strdup(GetExtendedStringUnsafe(
StructMEMORY_HEADER.size + StructEVENT_VALUE_DESCRIPTOR.size,
(int)log.Name));
fieldEntry->Value = (int)log.Value;
fieldEntry->FlagChar = (UCHAR)log.FlagLetter;
fieldEntry->ParentKey = parent;
EnumType * parentType = FindEnum(parent);
if (parentType == NULL) {
AddTempField(fieldEntry);
} else {
parentType->AddSymbolicValue(fieldEntry);
}
}
return;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
}
void ControllerObject::ReadFieldDescriptor(UINT64 entryAddress, UINT64 parent)
{
HRESULT status;
SEVENT_FIELD_DESCRIPTOR log;
EXT_CHECK(StructEVENT_FIELD_DESCRIPTOR.Read(entryAddress, &log));
FieldEntry * fieldEntry;
fieldEntry = new FieldEntry;
if (fieldEntry != NULL) {
fieldEntry->Name = _strdup(GetExtendedStringUnsafe(
StructMEMORY_HEADER.size + StructEVENT_FIELD_DESCRIPTOR.size,
(int)log.Name));
fieldEntry->Offset = (int)log.Offset;
fieldEntry->Type = (int)log.Type;
fieldEntry->ParentKey = parent;
EventTypeEntry * parentType = FindType(parent);
if (parentType == NULL) {
AddTempField(fieldEntry);
} else {
parentType->AddNewField(fieldEntry);
}
}
return;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
}
void ControllerObject::ReadGenericFieldDescriptor(UINT64 entryAddress, UINT64 parent)
{
HRESULT status;
SEVENT_GENERIC_TYPE_DESCRIPTOR log;
EXT_CHECK(StructEVENT_GENERIC_TYPE_DESCRIPTOR.Read(entryAddress, &log));
FieldEntry * fieldEntry;
fieldEntry = new FieldEntry;
if (fieldEntry != NULL) {
fieldEntry->Name = _strdup(GetExtendedStringUnsafe(
StructMEMORY_HEADER.size + StructEVENT_GENERIC_TYPE_DESCRIPTOR.size,
(int)log.Name));
fieldEntry->Offset = (int)log.Offset;
fieldEntry->Type = FIELD_TYPE_GENERIC_TYPE;
fieldEntry->ParentKey = parent;
fieldEntry->TypeKey = log.GenericTypeHandle;
fieldEntry->SymType = FindEnum(log.Type);
fieldEntry->Controller = this;
EventTypeEntry * parentType = FindType(parent);
if (parentType == NULL) {
AddTempField(fieldEntry);
} else {
parentType->AddNewField(fieldEntry);
}
}
return;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
}
UINT64 ControllerObject::ReadMemoryHeader(EventingEnumerator * enumerator,
UINT64 entryAddress)
{
HRESULT status;
SMEMORY_HEADER log;
ULONG64 CrtOffset;
EntryHeader header;
ReadSize = sizeof(EntryBuffer);
EXT_CHECK(StructMEMORY_HEADER.Read(entryAddress, &log));
header.address = entryAddress;
header.size = (int)log.Size;
header.timestamp = log.Timestamp;
header.Flags = (int)log.Flags;
header.TID = (int)log.TID;
header.Cpu = (int)log.Cpu;
header.StackSize = 0;
header.StackAddress = 0;;
CrtOffset = StructMEMORY_HEADER.size;
if ((log.Size) < ReadSize) {
ReadSize = log.Size;
}
Stacksize = 0;
MetadataSize = (ULONG_PTR)CrtOffset;
EXT_CHECK(TraceRead(entryAddress, EntryBuffer, (ULONG)ReadSize));
if (log.Flags & RECORD_STACK_TRACES) {
Stacksize = *(ULONG_PTR*)(EntryBuffer + (ULONG_PTR)CrtOffset) + 1;
header.StackSize = (int)Stacksize;
header.StackAddress = entryAddress + CrtOffset;
Stacksize *= sizeof(ULONG_PTR);
CrtOffset += Stacksize;
MetadataSize = (ULONG_PTR)CrtOffset;
}
if (log.Flags == RECORD_EVENT_TYPE) {
// Ignore the other internal structures that are not relevant for this extension
} else if (log.Flags == RECORD_EVENT_FIELD) {
// Ignore the other internal structures that are not relevant for this extension
} else if (log.Flags == RECORD_EVENT_SOURCE) {
// Ignore the other internal structures that are not relevant for this extension
} else if (log.Flags & RECORD_EVENT_METADATA_FLAG) {
// Ignore the other internal structures that are not relevant for this extension
} else if (log.Flags & RECORD_EVENT_TYPE_ARRAY_FLAG) {
EventTypeEntry * type = FindType(log.Type);
if (type != NULL) {
type->EnumerateEntries(enumerator, entryAddress + CrtOffset, (ULONG)log.TID);
}
} else {
EventTypeEntry * type = FindType(log.Type);
enumerator->EntryCallout(&header, type);
}
return log.Link;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return 0;
}
UINT64 ControllerObject::ReadMetadataMemoryHeader(EventingEnumerator * enumerator,
UINT64 entryAddress)
{
HRESULT status;
SMEMORY_HEADER log;
ULONG64 CrtOffset;
EntryHeader header;
ReadSize = sizeof(EntryBuffer);
EXT_CHECK(StructMEMORY_HEADER.Read(entryAddress, &log));
header.address = entryAddress;
header.size = (int)log.Size;
header.timestamp = log.Timestamp;
header.Flags = (int)log.Flags;
header.TID = (int)log.TID;
header.Cpu = (int)log.Cpu;
CrtOffset = StructMEMORY_HEADER.size;
if ((log.Size) < ReadSize) {
ReadSize = log.Size;
}
Stacksize = 0;
MetadataSize = (ULONG_PTR)CrtOffset;
EXT_CHECK(TraceRead(entryAddress, EntryBuffer, (ULONG)ReadSize));
ExtVerb("Reading entry %p\n", entryAddress);
if (log.Flags & RECORD_STACK_TRACES) {
Stacksize = *(ULONG_PTR*)(EntryBuffer + (ULONG_PTR)CrtOffset) + 1;
Stacksize *= sizeof(ULONG_PTR);
CrtOffset += Stacksize;
MetadataSize = (ULONG_PTR)CrtOffset;
}
if (log.Flags == RECORD_EVENT_TYPE) {
ReadEventDescriptor(entryAddress + CrtOffset);
} else if (log.Flags == RECORD_EVENT_FIELD) {
ReadFieldDescriptor(entryAddress + CrtOffset, log.Type);
} else if (log.Flags == RECORD_EVENT_SOURCE) {
ReadSource(entryAddress + CrtOffset);
} else if (log.Flags == RECORD_EVENT_GENERIC_FIELD) {
ReadGenericFieldDescriptor(entryAddress + CrtOffset, log.Type);
} else if (log.Flags == RECORD_EVENT_ENUM) {
ReadEnumDescriptor(entryAddress + CrtOffset);
} else if (log.Flags == RECORD_EVENT_VALUE) {
ReadValueDescriptor(entryAddress + CrtOffset, log.Type);
}
return log.Link;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return 0;
}
UINT64 ControllerObject::ReadMemoryZone(EventingEnumerator * enumerator, UINT64 zoneAddress)
{
HRESULT status;
SMEMORY_ZONE log;
EXT_CHECK(StructMEMORY_ZONE.Read(zoneAddress, &log));
UINT64 Blocks = (USHORT)log.ReadyList;
while (Blocks) {
Blocks = ReadMetadataMemoryHeader(enumerator, zoneAddress + Blocks);
}
Blocks = (USHORT)log.ReadyList;
while (Blocks) {
Blocks = ReadMemoryHeader(enumerator, zoneAddress + Blocks);
}
return log.Link;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return 0;
}
UINT64 ControllerObject::ReadStorage(EventingEnumerator * enumerator,
UINT64 storageAddress,
bool isRepository)
{
HRESULT status;
SMEMORY_STORAGE log;
if (storageAddress == 0) {
return 0;
}
EXT_CHECK(StructMEMORY_STORAGE.Read(storageAddress, &log));
ULONG64 CrtZone = log.MemoryZoneLink;
//
// We need to walk the repository storages anyway, regardless the
// the aditional filtering on storages.
//
if (enumerator->StorageCallout(storageAddress, FALSE) || isRepository) {
ExtVerb("Walking storage %p\n", storageAddress);
while (CrtZone) {
if (enumerator->ZoneCallout(CrtZone, FALSE)) {
ULONG64 NextZone = ReadMemoryZone(enumerator, CrtZone);
enumerator->ZoneCallout(CrtZone, TRUE);
if (CrtZone == NextZone) {
break;
}
CrtZone = NextZone;
} else {
break;
}
}
enumerator->StorageCallout(storageAddress, TRUE);
} else {
ExtVerb("Skipping storage %p\n", storageAddress);
}
return log.Link;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return 0;
}
ULONG64 SwitchToDomainAddressSpace(ULONG64 Context)
{
if (Context == 0) {
// Nothing to do if paging is not enabled
return 0;
}
// TODO: Implement me to handle the paging case
return 0;
}
void RevertToSystemDomainAddressSpace(ULONG64 Context)
{
if (Context == 0) {
// Nothing to do if paging is not enabled
return;
}
// TODO: Implement me to handle the paging case
}
void ControllerObject::FetchMetadata(EventingEnumerator * enumerator)
{
ReadStorage(enumerator, RepositoryAddress, true);
WalkTypes(enumerator);
WalkSources(enumerator);
WalkActiveSourcesEntries(enumerator);
}
void ControllerObject::WalkEntries(EventingEnumerator * enumerator)
{
UINT64 CrtStorage;
CrtStorage = StorageListHead;
while (CrtStorage) {
CrtStorage = ReadStorage(enumerator, CrtStorage, false);
}
}
void WalkTracingDatabase(EventingEnumerator * enumerator, bool typesOnly)
{
HRESULT status;
UINT64 controllerAddress = 0;
SSOURCE_CONTROLLER log;
ULONG64 SystemContextHandle;
ULONG64 ControllerLink;
FlushControllers();
if (!enumerator->SystemCallout(FALSE)) {
return;
}
EXT_CHECK(g_ExtSymbols->GetOffsetByName(kernel_SourceController, &controllerAddress));
EXT_CHECK(StructSOURCE_CONTROLLER.Read(controllerAddress, &log));
ControllerLink = log.ExternalControllers;
ControllerObject * ctrl = AllocateController(controllerAddress,
0,
log.SourceRepository,
log.StorageList);
if (enumerator->ControllerCallout(ctrl, FALSE)) {
if (ctrl != NULL) {
ctrl->FetchMetadata(enumerator);
if (enumerator->MedataCallout(ctrl) && !typesOnly) {
ctrl->WalkEntries(enumerator);
}
}
enumerator->ControllerCallout(ctrl, TRUE);
}
while ((DumpKernelOnly == FALSE) && (ControllerLink != 0)){
SEXTERNAL_CONTROLLER_DESCRIPTOR extController;
EXT_CHECK(StructEXTERNAL_CONTROLLER_DESCRIPTOR.Read(ControllerLink, &extController));
ControllerLink = extController.Link;
controllerAddress = extController.ControllerHandle;
SystemContextHandle = SwitchToDomainAddressSpace(extController.ContextHandle);
EXT_CHECK(StructSOURCE_CONTROLLER.Read(controllerAddress, &log));
ControllerObject * ctrl = AllocateController(controllerAddress,
extController.ContextHandle,
log.SourceRepository,
log.StorageList);
if (enumerator->ControllerCallout(ctrl, FALSE)) {
if (ctrl != NULL) {
ctrl->FetchMetadata(enumerator);
if (enumerator->MedataCallout(ctrl) && !typesOnly) {
ctrl->WalkEntries(enumerator);
}
}
enumerator->ControllerCallout(ctrl, TRUE);
}
RevertToSystemDomainAddressSpace(SystemContextHandle);
}
enumerator->SystemCallout(TRUE);
return;
Exit:
// Error path
ERRORBREAK("Invalid symbols information\n");
return;
}
UINT64 eventTypeHandle;
FieldFilter * filters[MAX_FILTERS];
int filtersCount = 0;
void CleanupFilters()
{
for (int i = 0; i < filtersCount; i++) {
delete (filters[i]);
}
filtersCount = 0;
}
class SourceFlagsEnumerator : public EventingEnumerator
{
StringPattern * FilterCollector;
StringPattern * FilterSource;
UINT Flags;
public:
SourceFlagsEnumerator(StringPattern * filterCollector, StringPattern * filterSource, UINT flags) {
FilterCollector = filterCollector;
FilterSource = filterSource;
Flags = flags;
}
bool virtual MedataCallout(ControllerObject *controller);
};
void
WriteULONG(ULONG64 address, ULONG value)
{
g_ExtData->WriteVirtualUncached(address, &value, sizeof(value), NULL);
}
bool SourceFlagsEnumerator::MedataCallout(ControllerObject *controller)
{
if (FilterCollector) {
if (!FilterCollector->IsMatch(controller->GetControllerName())) {
return FALSE;
}
}
if (FilterSource) {
for (int i = 0; i < controller->SourcesCount; i++) {
SourceEntry *src = controller->RegisteredSources[i];
if (FilterSource->IsMatch(src->Name)) {
ExtOut("Matched source (%s) (%s) %p %p\n",
controller->GetControllerName(),
src->Name,
src->Key,
(src->Key + _FieldsSOURCE_DESCRIPTOR[5].offset + StructMEMORY_HEADER.size));
WriteULONG((src->Key + _FieldsSOURCE_DESCRIPTOR[5].offset + StructMEMORY_HEADER.size),
Flags);
}
}
}
return FALSE;
}
void Tracing(PCSTR args)
{
PCSTR defaultVisibleFields = "|Timestamp|Cpu|TID|";
HRESULT status;
TypeFilterEnumerator * typeFilter = NULL;
CleanupFilters();
FlushTransforms();
eventTypeHandle = 0;
TypeEntryCollector * collector = NULL;
char * sortField = NULL;
EventingEnumerator * dump = NULL;
StringPattern * sourcePattern = NULL;
bool DumpSummary = false;
//
// Parse the remaining arguments. This stuff needs to be cleaned up or moved
// to dscript at some point.
//
DumpAllEvents = FALSE;
DumpStackTraces = FALSE;
DumpKernelOnly = FALSE;
DumpDescriptions = FALSE;
SKIP_WHITESPACES(args);
while (*args != '\0') {
SKIP_WHITESPACES(args);
// process argument
if (*args == '-' || *args == '/') {
args++;
switch (toupper(*args++)) {
case 'G':{
if ((*args == 'c') || (*args == 'C'))
{
EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::DeferedCommand", 2));
ExtOut("Kernel GC collection triggered. Press \'g\' to resume the execution.\n"
"It will break in debugger when GS collection completes\n");
}
else
{
SKIP_WHITESPACES(args);
// Write the GC logs buffer, with the memory size in MBytes
WriteCLRProfileFile(args, false);
}
goto CleanupAndExit;
}
break;
case 'E':{
bool enableKernel;
bool breakOnRecycle = false;
if ((*args == 'k') || (*args == 'K'))
{
enableKernel = true;
}
else if ((*args == 's') || (*args == 'S'))
{
enableKernel = false;
}
else
{
Usage();
goto CleanupAndExit;
}
// Consume the 'k' or 's' and skip whitespaces to the memory value
args += 1;
SKIP_WHITESPACES(args);
int MemorySize = (int)GetValue(args, false);
// There is an optional 'break' keyword
SKIP_WHITESPACES(args);
if (_stricmp(args, "break") == 0)
{
breakOnRecycle = true;
}
// Set the appropriate profiler buffer, with the memory size in MBytes & flags
if (enableKernel)
{
EnableKernelGCProfiler(MemorySize * 1024 * 1024, breakOnRecycle);
}
else
{
// Set the SIP profiler buffer, with the memory size in MBytes
EnableSIPGCProfiler(MemorySize * 1024 * 1024, breakOnRecycle);
}
goto CleanupAndExit;
}
break;
case 'D':{
if ((*args == 'k') || (*args == 'K'))
{
// Consume the k and skip whitespaces to the memory value
args += 1;
SKIP_WHITESPACES(args);
// Disable the profiler by setting the buffer size to 0
DisableKernelGCProfiler();
}
else if ((*args == 's') || (*args == 'S'))
{
// Consume the s and skip whitespaces to the memory value
args += 1;
SKIP_WHITESPACES(args);
int MemorySize = (int)GetValue(args, true);
// Disable the SIP profiler by resetting the buffer size
EnableSIPGCProfiler(0, false);
}
else
{
Usage();
}
goto CleanupAndExit;
}
break;
case 'M':
break;
case 'N':
DumpSummary = true;
break;
case 'A':{
DumpAllEvents = TRUE;
}
break;
case 'P':{
DumpDescriptions = TRUE;
}
break;
case 'K':{
DumpStackTraces= TRUE;
}
break;
case 'X':{
DumpKernelOnly = TRUE;
}
break;
case 'Y':{
ParseStringFormatTransform(args);
}
break;
case 'L':{
ParseSymbolLookupTransform(args);
}
break;
case 'R':{
defaultVisibleFields = NULL;
ParseVisibleFields(args);
}
break;
case 'T':{
SKIP_WHITESPACES(args);
DumpAllEvents = TRUE;
StringPattern * filter = new StringPattern();
if (filter == NULL) {
goto CleanupAndExit;
}
args = filter->ParseInput((char*)args);
typeFilter = new TypeFilterEnumerator();
if (typeFilter == NULL) {
delete filter;
goto CleanupAndExit;
}
typeFilter->SetPattern(filter);
WalkTracingDatabase(typeFilter, TRUE);
}
break;
case 'O':{
SKIP_WHITESPACES(args);
DumpAllEvents = TRUE;
char * sortField = ReadToken(args);
if (sortField != NULL) {
collector = new TypeEntryCollector(0, sortField);
}
}
break;
case 'Z':{
SKIP_WHITESPACES(args);
StringPattern * filterCollector = new StringPattern();
if (filterCollector == NULL) {
goto CleanupAndExit;
}
StringPattern * filterSource = new StringPattern();
if (filterSource == NULL) {
delete filterCollector;
goto CleanupAndExit;
}
args = filterCollector->ParseInput((char*)args);
args = filterSource->ParseInput((char*)args);
ULONG Flags = (int)GetValue(args, true);
SourceFlagsEnumerator *enumerator = new SourceFlagsEnumerator(filterCollector,
filterSource,
Flags);
if (enumerator != NULL) {
WalkTracingDatabase(enumerator, TRUE);
delete enumerator;
}
delete filterSource;
delete filterCollector;
goto CleanupAndExit;
}
break;
case 'S':{
SKIP_WHITESPACES(args);
DumpAllEvents = TRUE;
sourcePattern = new StringPattern();
if (sourcePattern == NULL) {
goto CleanupAndExit;
}
args = sourcePattern->ParseInput((char*)args);
}
break;
case 'F':{
SKIP_WHITESPACES(args);
DumpAllEvents = TRUE;
FieldFilter * filter = new FieldFilter();
if (filter == NULL) {
goto CleanupAndExit;
}
args = filter->ParseInput((char*)args);
filters[filtersCount++] = filter;
}
break;
}
SKIP_WHITESPACES(args);
} else {
Usage();
break;
}
}
if (defaultVisibleFields != NULL) {
defaultVisibleFields = NULL;
ParseVisibleFields(args);
}
if (typeFilter != 0) {
for (int i = 0; i < typeFilter->Array->InUse; i++) {
dump = new DumpType(typeFilter->Array->Array[i]);
if (dump) {
dump->ApplyFilters(filters, filtersCount);
dump->ApplySourceSelector(sourcePattern);
if (collector == NULL) {
//
// If no other order criteria has been established
// default to sort by timestamp
//
collector = new TypeEntryCollector(0, "Timestamp");
}
if (collector) {
collector->TypeHandle = typeFilter->Array->Array[i];
collector->cascadeEnumerator = dump;
collector->GroupByController = TRUE;
WalkTracingDatabase(collector, FALSE);
} else {
WalkTracingDatabase(dump, FALSE);
}
delete dump;
dump = NULL;
}
}
} else {
dump = new DumpTracing();
if (dump) {
((DumpTracing*)dump)->DumpSummary = DumpSummary;
dump->ApplyFilters(filters, filtersCount);
dump->ApplySourceSelector(sourcePattern);
if (collector == NULL) {
//
// If no other order criteria has been established
// default to sort by timestamp
//
collector = new TypeEntryCollector(0, "Timestamp");
}
if (collector) {
collector->TypeHandle = 0;
collector->cascadeEnumerator = dump;
if (DumpSummary) {
collector->GroupByController = TRUE;
}
WalkTracingDatabase(collector, FALSE);
} else {
WalkTracingDatabase(dump, !DumpAllEvents);
}
}
}
Exit:
CleanupAndExit:
if (sortField) free(sortField);
if (dump) delete dump;
if (typeFilter) delete typeFilter;
if (sourcePattern) delete sourcePattern;
return;
}
EXT_DECL(diagnose) // Defines: PDEBUG_CLIENT Client, PCSTR args
{
EXT_ENTER(); // Defines: HRESULT status = S_OK;
bool KernelProfile = true;
pointerSize = (g_ExtControl->IsPointer64Bit() == S_OK) ? 8 : 4;
SKIP_WHITESPACES(args);
if (*args == '\0')
{
Usage();
} else {
Tracing(args);
}
EXT_LEAVE(); // Macro includes: return status;
}