//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: MemoryStorage.cpp // // Note: Kernel & Process // #include "hal.h" #include "eventing.h" #ifndef SINGULARITY_KERNEL void DebugPrintEvent(UIntPtr eventHandle) { Class_Microsoft_Singularity_Eventing_SystemControllerProxy::g_DebugPrintLogEntry(eventHandle); } #endif uint32 MemoryStorageGetNextGeneration(PMEMORY_STORAGE Storage) { return (uint32)InterlockedIncrement((volatile INT32* )&Storage->Generation); } PMEMORY_HEADER MemoryStorageAdvanceCursor(PMEMORY_STORAGE Storage, PMEMORY_ZONE CurrentCursor, uint16 size) { PMEMORY_ZONE NewCursor; PMEMORY_ZONE CapturedCursor = Storage->ZoneCursor; BOOL Reycled = false; NewCursor = CurrentCursor; if (NewCursor == NULL) { NewCursor = Storage->MemoryZoneLink; } for (NewCursor = NewCursor->Link; NewCursor != CurrentCursor; NewCursor = NewCursor->Link) { if (NewCursor == NULL) { // Restart from the begining of the storage // Make sure the policy allows this if (Storage->Flags & MEMORY_STORAGE_FLAGS_RECYCLE_MEMORY) { if (Reycled) { // Every zone has been tried for this allocation // return back the failure. return NULL; } if (Storage->Flags & MEMORY_STORAGE_FLAGS_BREAK_ON_RECYCLE) { // If we trap here, there is a request to stop before over-writing // any buffer contents. That is about to happen, so this is the // last chance to retrieve those contents before they are lost. // // Then proceed from this breakpoint. __debugbreak(); } Reycled = true; NewCursor = Storage->MemoryZoneLink; } else { // We must return here failure, as recycling the memory for older events // is not permited return NULL; } } if (IsZoneCompleted(NewCursor)) { RecycleZone(NewCursor); } PMEMORY_HEADER Event = AllocateEventEntry(NewCursor, size); if (Event != NULL) { // we were successfuly in using this zone, set it as current cursor // It is possible some other thread already did it, or even moved far ahead // But this should not be a problem as there are no guarantees wrt. ordering // in the storage InterlockedCompareExchangePointer((PVOID *)&Storage->ZoneCursor, (PVOID)NewCursor, (PVOID)CapturedCursor); return Event; } } return NULL; } uint32 CaptureStackTrace(UIntPtr * StackArray, UINT MaxStackSize) { uint32 Index; UIntPtr CurrentFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFramePointer(); UIntPtr CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CurrentFrame); Struct_Microsoft_Singularity_ProcessorContext *processorContext = Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext(); if ((CurrentFrame <= (UIntPtr)processorContext->cpuRecord.interruptStackBegin) && (CurrentFrame > (UIntPtr)processorContext->cpuRecord.interruptStackLimit)) { // Capture the interrupte stack. Always check against the limits // before fetching the next frame for (Index = 0; (CallerFrame != 0) && Index < MaxStackSize; Index += 1) { if ((CallerFrame > (UIntPtr)processorContext->cpuRecord.interruptStackBegin) || (CallerFrame < (UIntPtr)processorContext->cpuRecord.interruptStackLimit)) { return Index; } StackArray[Index] = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameReturnAddress(CallerFrame); CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CallerFrame); } return Index; } for (Index = 0; (CallerFrame != 0) && Index < MaxStackSize; Index += 1) { StackArray[Index] = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameReturnAddress(CallerFrame); CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CallerFrame); } return Index; } PMEMORY_HEADER InternalLogRecord(UIntPtr StorageHandle, uint32 Flags, UIntPtr eventType, PVOID Buffer, uint32 size, PVOID * ExtendedBuffer, uint32 ExtendedSize ) { if (StorageHandle == 0) return NULL; PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)StorageHandle; UIntPtr Stacks[RECORD_MAXSTACKSIZE]; uint32 StackSize = 0; uint32 allocSize = (uint32)ROUND_UP_TO_POWER2(size, sizeof(UIntPtr)); if (Flags & RECORD_STACK_TRACES) { StackSize = CaptureStackTrace(Stacks, RECORD_MAXSTACKSIZE); if (StackSize) { // There is no point to save the eip of the current function in the database // since is redundant information. Reuse instead that slot to keep the stack size Stacks[0] = (UIntPtr)(StackSize - 1); // Convert to memory usage StackSize *= sizeof(UIntPtr); } else { // clear the flag as there is no stack available Flags &= ~RECORD_STACK_TRACES; } } PMEMORY_ZONE Zone = Storage->ZoneCursor; if (Zone == NULL) { Zone = Storage->MemoryZoneLink; } UINT32 EntrySize = GetRecordHeaderSize (Flags, StackSize); PMEMORY_HEADER Event = AllocateEventEntry(Zone, (uint16)(EntrySize + allocSize + ExtendedSize - sizeof(MEMORY_HEADER))); if (Event == NULL) { // The zone is filled up, advance the cursor to the next zone Event = MemoryStorageAdvanceCursor(Storage, Zone, (uint16)(EntrySize + allocSize + ExtendedSize - sizeof(MEMORY_HEADER))); } if (Event != NULL) { // Copy the entry flags, which also include the layout description Event->Flags = (uint16)Flags; Event->Type = eventType; // Handle the stack traces, if present UCHAR * Dest = (UCHAR *)GetRecordInternalStructure(Event, RECORD_STACK_TRACES); if (Dest != NULL) { memcpy(Dest, Stacks, StackSize); } // Copy the remaining portion provided by the user to the buffer if (Buffer) { memcpy((char *)Event + EntrySize, Buffer, size); } if (ExtendedSize != 0) { *ExtendedBuffer = (char *)Event + EntrySize + allocSize; } } return Event; } PMEMORY_HEADER InternalLogFixedRecord(UIntPtr StorageHandle, uint32 Flags, UIntPtr eventType, PVOID Buffer, uint32 size) { PMEMORY_HEADER entry = InternalLogRecord(StorageHandle, Flags, eventType, Buffer, size, NULL, 0); if (entry != NULL) { if (Flags & Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_DEBUG_PRINT){ DebugPrintEvent((UIntPtr)entry); } CommitEventEntry(entry); } return entry; } PMEMORY_HEADER InternalLogVariableRecord( bool doCommit, UIntPtr StorageHandle, uint32 Flags, UIntPtr eventType, PVOID Buffer, uint32 size, int32 variableItemsCount, Struct_Microsoft_Singularity_Eventing_ArrayType * variableItems) { if (StorageHandle == 0) return NULL; PVOID ExtendedBuffer; int i; int arrayDescriptorSize = variableItemsCount * sizeof(uint16); int32 extendedSize = arrayDescriptorSize; for (i = 0; i < variableItemsCount; i++) { extendedSize += (int32)variableItems[i].Length; if ((variableItems[i].Type == EVENT_FIELD_TYPE_string) || (variableItems[i].Type == EVENT_FIELD_TYPE_szChar)) { // Account for the null terminator that ConvertToChars automatically // inserts to the string. For szChar data, the string will also have one // null character at the end, that has not been encounted for extendedSize += 1; } } PMEMORY_HEADER Entry = InternalLogRecord(StorageHandle, Flags, eventType, Buffer, size, &ExtendedBuffer, extendedSize); if (Entry == NULL) { return Entry; } if (extendedSize > 0) { // Note the test for extendedSize. This is the only case where we are // allowed to add something after calling InternalLogRecord. We are also required // explicitely commit the entry when we're done for (i = 0; i < variableItemsCount; i++) { // // For strings, the mechanism wil automatically extend the buffer with one // extra character to include the null terminator. The length in the field // should be however consistent and report the correct length of the buffer // if (variableItems[i].Type == EVENT_FIELD_TYPE_string) { // Account for the null terminator that ConvertToChars automatically // inserts to the string. unsigned short adjustedLength = variableItems[i].Length + 1; C_ASSERT(sizeof(adjustedLength) == sizeof(variableItems[i].Length)); memcpy(ExtendedBuffer, &adjustedLength, sizeof(adjustedLength)); ExtendedBuffer = (char *)ExtendedBuffer + sizeof(variableItems[i].Length); // If this was a bartok string, convert it to ascii ExtendedBuffer = ConvertToChars((char *)ExtendedBuffer, (bartok_char *)variableItems[i].Buffer, (int32)variableItems[i].Length); EV_ASSERT(ExtendedBuffer <= (PVOID)((char*)Entry + Entry->Size )); } else if (variableItems[i].Type == EVENT_FIELD_TYPE_szChar) { // Account for the null terminator that is automatically // inserted to the string. unsigned short adjustedLength = variableItems[i].Length + 1; // // Save the total size in bytes and save it to the entry. Move the pointer forward // C_ASSERT(sizeof(adjustedLength) == sizeof(variableItems[i].Length)); memcpy(ExtendedBuffer, &adjustedLength, sizeof(adjustedLength)); ExtendedBuffer = (char *)ExtendedBuffer + sizeof(adjustedLength); // // Copy now the actual content of the buffer // memcpy(ExtendedBuffer, variableItems[i].Buffer, (int32)variableItems[i].Length); ExtendedBuffer = (char *)ExtendedBuffer + variableItems[i].Length; *(char *)ExtendedBuffer = (char)0; ExtendedBuffer = (char *)ExtendedBuffer + 1; } else { // // Save the length of the data and advance the pointer // memcpy(ExtendedBuffer, &variableItems[i].Length, sizeof(variableItems[i].Length)); ExtendedBuffer = (char *)ExtendedBuffer + sizeof(variableItems[i].Length); // nothing else to do here, just copy the content and advance the pointer memcpy(ExtendedBuffer, variableItems[i].Buffer, (int32)variableItems[i].Length); ExtendedBuffer = (char *)ExtendedBuffer + variableItems[i].Length; } EV_ASSERT(ExtendedBuffer <= (PVOID)((char*)Entry + Entry->Size )); } } if (Flags & Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_DEBUG_PRINT){ DebugPrintEvent((UIntPtr)Entry); } if (doCommit) { CommitEventEntry(Entry); } return Entry; } UIntPtr OpenLoggingStorage(UIntPtr sourceHandle, uint32 * flags) { if (sourceHandle == 0) return 0; PSOURCE_DESCRIPTOR source = HANDLE_TO_SOURCE(sourceHandle); if (((source->ControlFlags & (*flags)) >> 16) == 0) return 0; *flags |= source->ControlFlags; return source->StorageHandle; } PMEMORY_ZONE GetNextZone(PQUERY_VIEW view) { if (view->StartZone == NULL) { return NULL; } for (;;) { if (view->Forward) { view->CurrentZone = view->CurrentZone->Link; if (view->CurrentZone == NULL) { view->CurrentZone = view->Storage->MemoryZoneLink; } } else { view->CurrentZone = view->CurrentZone->BkLink; if (view->CurrentZone == NULL) { view->CurrentZone = view->Storage->BkLink; } } if (view->CurrentZone == view->StartZone) { return NULL; } // ??? handle the generation wrap if (view->CurrentZone->Generation <= view->QueryGeneration) { view->CurrentEntryIndex = 0; view->ZoneGeneration = view->CurrentZone->Generation; return view->CurrentZone; } } } PQUERY_VIEW CreateQuery(UIntPtr storageHandle, bool forward) { PQUERY_VIEW queryView = AllocateQueryView( ); if (queryView) { queryView->Storage = HANDLE_TO_STORAGE(storageHandle); queryView->Forward = forward; queryView->QueryGeneration = queryView->Storage->Generation; queryView->CurrentZone = NULL; queryView->ZoneGeneration = 0; queryView->CurrentEntry = NULL; queryView->CurrentEntryIndex = 0; queryView->EndOfBuffer = false; if (forward) { queryView->StartZone = queryView->Storage->ZoneCursor; if (queryView->StartZone != NULL) { queryView->StartZone = queryView->StartZone->Link; } if (queryView->StartZone == NULL) { queryView->StartZone = queryView->Storage->MemoryZoneLink; } } else { queryView->StartZone = queryView->Storage->ZoneCursor; if (queryView->StartZone == NULL) { queryView->StartZone = queryView->Storage->BkLink; } } queryView->CurrentZone = queryView->StartZone; queryView->QueryReset = true; } return queryView; } PMEMORY_HEADER GetNextStorageEntry(PQUERY_VIEW queryView) { PMEMORY_HEADER Entry; if (queryView->EndOfBuffer) { return NULL; } if (queryView->CurrentEntry) { Entry = GetNextEntry(queryView); if (Entry) { return Entry; } } PMEMORY_ZONE zone; if (queryView->QueryReset) { zone = queryView->StartZone; queryView->QueryReset = false; queryView->ZoneGeneration = zone->Generation; queryView->CurrentEntryIndex = 0; } else { zone = GetNextZone(queryView); } while (zone != NULL) { queryView->CurrentEntry = GetFirstEntry(zone, queryView->Forward); if (queryView->CurrentEntry) { return queryView->CurrentEntry; } zone = GetNextZone(queryView); } queryView->EndOfBuffer = true; return NULL; } // // ABI calls for Eventing.MemoryStorage // UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_MemoryStorageCreateImpl(uint32 Flags, UCHAR * InitialBuffer, uint32 BufferSize, uint32 ZoneSize) { if (BufferSize <= sizeof(MEMORY_STORAGE)) { return 0; } PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)InitialBuffer; Storage->StorageSize = 0; Storage->MemoryZoneLink = NULL; Storage->ZoneCursor = NULL; Storage->Flags = Flags; Storage->BkLink = NULL; Storage->Generation = 0; ZoneSize = (uint32)ROUND_UP_TO_POWER2(ZoneSize, EV_ZONE_ALIGNMENT); if (((Flags & 0xF) == MEMORY_STORAGE_FLAGS_PERMANENT) || ((Flags & 0xF) == MEMORY_STORAGE_FLAGS_ACTIVE_STORAGE) ) { ZoneSize = BufferSize; } if (ZoneSize < sizeof(MEMORY_ZONE)) { ZoneSize = BufferSize / EV_DEFAULT_ZONE_BUFFER_RATIO; ZoneSize = (uint32)ROUND_UP_TO_POWER2(ZoneSize, EV_ZONE_ALIGNMENT); } if (ZoneSize > EV_MAXIMUM_ZONE_SIZE) { Storage->DefaultZoneSize = EV_MAXIMUM_ZONE_SIZE; } else { Storage->DefaultZoneSize = ZoneSize; } Storage->ZoneCount = 0; InitialBuffer = (UCHAR *)(Storage + 1); InitialBuffer = (UCHAR *)ROUND_UP_TO_POWER2(InitialBuffer, EV_ZONE_ALIGNMENT); Class_Microsoft_Singularity_Eventing_MemoryStorage::g_MemoryStorageRegisterBufferImpl( (UIntPtr)Storage, InitialBuffer, (uint32)(BufferSize - ((uint8 *)InitialBuffer - (uint8 *)Storage))); Storage->ZoneCursor = Storage->MemoryZoneLink; return (UIntPtr)Storage; } void Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_MemoryStorageRegisterBufferImpl(UIntPtr StorageHandle, UCHAR * Buffer, uint32 BufferSize) { PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)StorageHandle; PMEMORY_ZONE LastZone = NULL; PMEMORY_ZONE ZoneChain = NULL; PMEMORY_ZONE TempZone; PMEMORY_ZONE LastExistingZone = Storage->BkLink; InterlockedExchangeAdd((volatile INT32 *)&Storage->StorageSize, BufferSize); if ((Storage->DefaultZoneSize != 0) && (BufferSize > Storage->DefaultZoneSize)) { // We have to split the larger chunk in multiple zones uint32 MaxZone = BufferSize / Storage->DefaultZoneSize; for (uint32 i = 0; i < MaxZone; i++) { TempZone = InitializeMemoryZone(Buffer, Storage->DefaultZoneSize, StorageHandle); EV_ASSERT(TempZone != NULL); InterlockedIncrement((volatile INT32* )&Storage->ZoneCount); if (ZoneChain == NULL) { ZoneChain = TempZone; } // Link the zones togeather. Note the chain is not yet published outside // until is pushed to the memory storage list eventually if (LastZone) { LastZone->Link = TempZone; } TempZone->BkLink = LastZone; LastZone = TempZone; Buffer = (UCHAR *)Buffer + Storage->DefaultZoneSize; BufferSize -= Storage->DefaultZoneSize; } } TempZone = InitializeMemoryZone(Buffer, BufferSize, StorageHandle); if (TempZone != NULL) { InterlockedIncrement((volatile INT32 *)&Storage->ZoneCount); // Here the remining portion might be too small for a whole zone. if (ZoneChain == NULL) { ZoneChain = TempZone; } // Link the zones togeather. Note the chain is not yet published outside // until is pushed to the memory storage list eventually if (LastZone) { LastZone->Link = TempZone; } TempZone->BkLink = LastZone; LastZone = TempZone; } if (LastZone != NULL) { LastZone->Link = NULL; if (LastExistingZone == NULL) { // The existing list is empty. Insert the new one to the head ZoneChain->BkLink = NULL; Storage->BkLink = LastZone; Storage->MemoryZoneLink = ZoneChain; } else { LastExistingZone->Link = ZoneChain; ZoneChain->BkLink = LastExistingZone; } Storage->BkLink = LastZone; } } UIntPtr Class_Microsoft_Singularity_Eventing_EventSource:: g_LogSourceEntryImpl(UIntPtr sourceHandle, uint32 flags, UIntPtr eventType, uint8 * Buffer, int32 size) { UIntPtr storageHandle = OpenLoggingStorage(sourceHandle, &flags); return (UIntPtr)InternalLogFixedRecord(storageHandle, flags, eventType, Buffer, size); } UIntPtr Class_Microsoft_Singularity_Eventing_EventSource:: g_LogSourceEntryImpl(UIntPtr sourceHandle, uint32 flags, UIntPtr eventType, uint8 * Buffer, int32 size, int32 arraysCount, Struct_Microsoft_Singularity_Eventing_ArrayType * arrays) { UIntPtr storageHandle = OpenLoggingStorage(sourceHandle, &flags); return (UIntPtr)InternalLogVariableRecord(true, storageHandle, flags, eventType, Buffer, size, arraysCount, arrays); } uint32 Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_GetMemoryStorageOveheadImpl() { // Determine the overhead in the pesimistic case return sizeof(MEMORY_STORAGE) + (sizeof(MEMORY_ZONE) + EV_ZONE_ALIGNMENT) * EV_DEFAULT_ZONE_BUFFER_RATIO + sizeof(MEMORY_HEADER) * EV_DEFAULT_ZONE_BUFFER_RATIO; } UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_CreateQueryViewImpl(UIntPtr storageHandle, bool forward) { return (UIntPtr)CreateQuery(storageHandle, forward); } void Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_DeleteQueryViewImpl(UIntPtr queryHandle) { UnRegisterQueryView((PQUERY_VIEW)queryHandle); } UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_GetNextEntryImpl(UIntPtr queryHandle, UIntPtr * typeHandle, uint32 * userOffset, uint8 * buffer, uint16 bufferSize) { PQUERY_VIEW view = (PQUERY_VIEW)queryHandle; PMEMORY_HEADER entry = GetNextStorageEntry(view); if (entry != NULL) { *typeHandle = entry->Type; void * src = GetUserRecordStructure(entry); if (entry->Size < bufferSize) { bufferSize = entry->Size; } memcpy(buffer, entry , bufferSize); *userOffset = (uint32)((ULONG_PTR)src - (ULONG_PTR)entry); } return (UIntPtr) entry; } UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: g_WalkEventDescriptorImpl(UIntPtr eventHandle, UIntPtr currentField, uint16 * offset, uint16 * type, bartok_char * bufferName, uint16 bufferSize) { if (eventHandle == 0) { eventHandle = Handle_MEMORY_HEADER; } PMEMORY_HEADER Entry = HANDLE_TO_HEADER(eventHandle); EV_ASSERT(Entry->Flags == RECORD_EVENT_TYPE); PEVENT_DESCRIPTOR eventDescriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(Entry); PEVENT_FIELD_DESCRIPTOR field = NULL; char * src; if (currentField == 0) { src = GetExtendedString(eventHandle, 1); } else if (currentField == eventHandle) { // start now with the fields inside the event field = eventDescriptor->fieldsLink; } else { field = (PEVENT_FIELD_DESCRIPTOR)(currentField); field = field->fieldsLink; if (field == NULL) { return 0; } } if (field == NULL) { // This is refers the event descriptor itself. // The type returned will be zero, the only meaningful information // will be the event name *offset = 0; *type = 0; } else { *offset = field->Offset; *type = field->Type; src = GetExtendedString((UIntPtr)((PMEMORY_HEADER)field - 1), 1); } if ((bufferName != NULL) && (bufferSize != 0)) { while ((bufferSize != 0) && (*src)) { *bufferName++ = *src++; bufferSize -= sizeof(bartok_char); } if (bufferSize == 0) { // Move back one position to insert the null terminator. // We have at least this character in the buffer due to the test two levels above bufferSize -= sizeof(bartok_char); } *bufferName = 0; } if (field == NULL) { return eventHandle; } return (UIntPtr)field; } // ///////////////////////////////////////////////////////////////// End of File.