///////////////////////////////////////////////////////////////////////////// // // 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 ] [-t ] [-o ] [-f ] [-f ] [...]\n" "\n" " Filter format:\n" " -f [<=,<,>,!> ]\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 : Write all profile logs that have been collected in files\n" " named based upon provided prefix\n" " -ek [break] : Enable kernel GC profiling with a buffer of specified size in MBytes.\n" " Optionally break into the debugger before recycling buffers\n" " -es [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; }