/** Writes performance data of booting into the allocated memory. OS can process these records. @param Event The triggered event. @param Context Context for this event. **/ VOID EFIAPI WriteBootToOsPerformanceData ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; UINT32 LimitCount; EFI_HANDLE *Handles; UINTN NoHandles; CHAR8 GaugeString[PERF_TOKEN_SIZE]; UINT8 *Ptr; UINT32 Index; UINT64 Ticker; UINT64 Freq; UINT32 Duration; UINTN LogEntryKey; CONST VOID *Handle; CONST CHAR8 *Token; CONST CHAR8 *Module; UINT64 StartTicker; UINT64 EndTicker; UINT64 StartValue; UINT64 EndValue; BOOLEAN CountUp; UINTN EntryIndex; UINTN NumPerfEntries; // // List of flags indicating PerfEntry contains DXE handle // BOOLEAN *PerfEntriesAsDxeHandle; UINTN VarSize; // // Record the performance data for End of BDS // PERF_END(NULL, "BDS", NULL, 0); // // Retrieve time stamp count as early as possible // Ticker = GetPerformanceCounter (); Freq = GetPerformanceCounterProperties (&StartValue, &EndValue); Freq = DivU64x32 (Freq, 1000); mPerfHeader.CpuFreq = Freq; // // Record BDS raw performance data // if (EndValue >= StartValue) { mPerfHeader.BDSRaw = Ticker - StartValue; CountUp = TRUE; } else { mPerfHeader.BDSRaw = StartValue - Ticker; CountUp = FALSE; } if (mAcpiLowMemoryBase == 0x0FFFFFFFF) { VarSize = sizeof (EFI_PHYSICAL_ADDRESS); Status = gRT->GetVariable ( L"PerfDataMemAddr", &gPerformanceProtocolGuid, NULL, &VarSize, &mAcpiLowMemoryBase ); if (EFI_ERROR (Status)) { // // Fail to get the variable, return. // return; } } // // Put Detailed performance data into memory // Handles = NULL; Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &NoHandles, &Handles ); if (EFI_ERROR (Status)) { return ; } Ptr = (UINT8 *) ((UINT32) mAcpiLowMemoryBase + sizeof (PERF_HEADER)); LimitCount = (UINT32) (PERF_DATA_MAX_LENGTH - sizeof (PERF_HEADER)) / sizeof (PERF_DATA); NumPerfEntries = 0; LogEntryKey = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { NumPerfEntries++; } PerfEntriesAsDxeHandle = AllocateZeroPool (NumPerfEntries * sizeof (BOOLEAN)); ASSERT (PerfEntriesAsDxeHandle != NULL); // // Get DXE drivers performance // for (Index = 0; Index < NoHandles; Index++) { Ticker = 0; LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (Handle == Handles[Index] && !PerfEntriesAsDxeHandle[EntryIndex]) { PerfEntriesAsDxeHandle[EntryIndex] = TRUE; } EntryIndex++; if ((Handle == Handles[Index]) && (EndTicker != 0)) { if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker += CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); } } Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); if (Duration > 0) { GetNameFromHandle (Handles[Index], GaugeString); AsciiStrCpyS (mPerfData.Token, PERF_TOKEN_SIZE, GaugeString); mPerfData.Duration = Duration; CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } } // // Get inserted performance data // LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (!PerfEntriesAsDxeHandle[EntryIndex] && EndTicker != 0) { ZeroMem (&mPerfData, sizeof (PERF_DATA)); AsciiStrnCpyS (mPerfData.Token, PERF_TOKEN_SIZE, Token, PERF_TOKEN_LENGTH); if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker = CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); mPerfData.Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } EntryIndex++; } Done: FreePool (Handles); FreePool (PerfEntriesAsDxeHandle); mPerfHeader.Signiture = PERFORMANCE_SIGNATURE; // // Put performance data to Reserved memory // CopyMem ( (UINTN *) (UINTN) mAcpiLowMemoryBase, &mPerfHeader, sizeof (PERF_HEADER) ); return ; }
/** Simple arm disassembler via a library Argv[0] - disasm Argv[1] - Address to start disassembling from ARgv[2] - Number of instructions to disassembly (optional) @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblPerformance ( IN UINTN Argc, IN CHAR8 **Argv ) { UINTN Key; CONST VOID *Handle; CONST CHAR8 *Token, *Module; UINT64 Start, Stop, TimeStamp; UINT64 Delta, TicksPerSecond, Milliseconds, Microseconds; UINTN Index; BOOLEAN CountUp; TicksPerSecond = GetPerformanceCounterProperties (&Start, &Stop); if (Start < Stop) { CountUp = TRUE; } else { CountUp = FALSE; } Key = 0; do { Key = GetPerformanceMeasurement (Key, (CONST VOID **)&Handle, &Token, &Module, &Start, &Stop); if (Key != 0) { if (AsciiStriCmp ("StartImage:", Token) == 0) { if (Stop == 0) { // The entry for EBL is still running so the stop time will be zero. Skip it AsciiPrint (" running %a\n", ImageHandleToPdbFileName ((EFI_HANDLE)Handle)); } else { Delta = CountUp?(Stop - Start):(Start - Stop); Microseconds = DivU64x64Remainder (MultU64x32 (Delta, 1000000), TicksPerSecond, NULL); AsciiPrint ("%10ld us %a\n", Microseconds, ImageHandleToPdbFileName ((EFI_HANDLE)Handle)); } } } } while (Key != 0); AsciiPrint ("\n"); TimeStamp = 0; Key = 0; do { Key = GetPerformanceMeasurement (Key, (CONST VOID **)&Handle, &Token, &Module, &Start, &Stop); if (Key != 0) { for (Index = 0; mTokenList[Index] != NULL; Index++) { if (AsciiStriCmp (mTokenList[Index], Token) == 0) { Delta = CountUp?(Stop - Start):(Start - Stop); TimeStamp += Delta; Milliseconds = DivU64x64Remainder (MultU64x32 (Delta, 1000), TicksPerSecond, NULL); AsciiPrint ("%6a %6ld ms\n", Token, Milliseconds); break; } } } } while (Key != 0); AsciiPrint ("Total Time = %ld ms\n\n", DivU64x64Remainder (MultU64x32 (TimeStamp, 1000), TicksPerSecond, NULL)); return EFI_SUCCESS; }
/** Allocates a block of memory and writes performance data of booting into it. OS can processing these record. **/ VOID WriteBootToOsPerformanceData ( VOID ) { EFI_STATUS Status; UINT32 AcpiLowMemoryLength; UINT32 LimitCount; EFI_HANDLE *Handles; UINTN NoHandles; CHAR8 GaugeString[PERF_TOKEN_LENGTH]; UINT8 *Ptr; UINT32 Index; UINT64 Ticker; UINT64 Freq; UINT32 Duration; UINTN LogEntryKey; CONST VOID *Handle; CONST CHAR8 *Token; CONST CHAR8 *Module; UINT64 StartTicker; UINT64 EndTicker; UINT64 StartValue; UINT64 EndValue; BOOLEAN CountUp; UINTN EntryIndex; UINTN NumPerfEntries; // // List of flags indicating PerfEntry contains DXE handle // BOOLEAN *PerfEntriesAsDxeHandle; // // Retrieve time stamp count as early as possible // Ticker = GetPerformanceCounter (); Freq = GetPerformanceCounterProperties (&StartValue, &EndValue); Freq = DivU64x32 (Freq, 1000); mPerfHeader.CpuFreq = Freq; // // Record BDS raw performance data // if (EndValue >= StartValue) { mPerfHeader.BDSRaw = Ticker - StartValue; CountUp = TRUE; } else { mPerfHeader.BDSRaw = StartValue - Ticker; CountUp = FALSE; } // // Put Detailed performance data into memory // Handles = NULL; Status = gBS->LocateHandleBuffer ( AllHandles, NULL, NULL, &NoHandles, &Handles ); if (EFI_ERROR (Status)) { return ; } AcpiLowMemoryLength = 0x4000; if (mAcpiLowMemoryBase == 0x0FFFFFFFF) { // // Allocate a block of memory that contain performance data to OS // Status = gBS->AllocatePages ( AllocateMaxAddress, EfiReservedMemoryType, EFI_SIZE_TO_PAGES (AcpiLowMemoryLength), &mAcpiLowMemoryBase ); if (EFI_ERROR (Status)) { FreePool (Handles); return ; } } Ptr = (UINT8 *) ((UINT32) mAcpiLowMemoryBase + sizeof (PERF_HEADER)); LimitCount = (AcpiLowMemoryLength - sizeof (PERF_HEADER)) / sizeof (PERF_DATA); NumPerfEntries = 0; LogEntryKey = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { NumPerfEntries++; } PerfEntriesAsDxeHandle = AllocateZeroPool (NumPerfEntries * sizeof (BOOLEAN)); ASSERT (PerfEntriesAsDxeHandle != NULL); // // Get DXE drivers performance // for (Index = 0; Index < NoHandles; Index++) { Ticker = 0; LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (Handle == Handles[Index] && !PerfEntriesAsDxeHandle[EntryIndex]) { PerfEntriesAsDxeHandle[EntryIndex] = TRUE; } EntryIndex++; if ((Handle == Handles[Index]) && (EndTicker != 0)) { if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker += CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); } } Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); if (Duration > 0) { GetNameFromHandle (Handles[Index], GaugeString); AsciiStrCpy (mPerfData.Token, GaugeString); mPerfData.Duration = Duration; CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } } // // Get inserted performance data // LogEntryKey = 0; EntryIndex = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (!PerfEntriesAsDxeHandle[EntryIndex] && EndTicker != 0) { ZeroMem (&mPerfData, sizeof (PERF_DATA)); AsciiStrnCpy (mPerfData.Token, Token, PERF_TOKEN_LENGTH); if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker = CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); mPerfData.Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); CopyMem (Ptr, &mPerfData, sizeof (PERF_DATA)); Ptr += sizeof (PERF_DATA); mPerfHeader.Count++; if (mPerfHeader.Count == LimitCount) { goto Done; } } EntryIndex++; } Done: FreePool (Handles); FreePool (PerfEntriesAsDxeHandle); mPerfHeader.Signiture = PERFORMANCE_SIGNATURE; // // Put performance data to Reserved memory // CopyMem ( (UINTN *) (UINTN) mAcpiLowMemoryBase, &mPerfHeader, sizeof (PERF_HEADER) ); gRT->SetVariable ( L"PerfDataMemAddr", &gPerformanceProtocolGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (EFI_PHYSICAL_ADDRESS), &mAcpiLowMemoryBase ); return ; }
/** Performance measure function to get S3 detailed performance data. This function will getS3 detailed performance data and saved in pre-reserved ACPI memory. **/ VOID WriteToOsS3PerformanceData ( VOID ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS mAcpiLowMemoryBase; PERF_HEADER *PerfHeader; PERF_DATA *PerfData; UINT64 Ticker; UINTN Index; EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices; UINTN VarSize; UINTN LogEntryKey; CONST VOID *Handle; CONST CHAR8 *Token; CONST CHAR8 *Module; UINT64 StartTicker; UINT64 EndTicker; UINT64 StartValue; UINT64 EndValue; BOOLEAN CountUp; UINT64 Freq; // // Retrive time stamp count as early as possilbe // Ticker = GetPerformanceCounter (); Freq = GetPerformanceCounterProperties (&StartValue, &EndValue); Freq = DivU64x32 (Freq, 1000); Status = PeiServicesLocatePpi ( &gEfiPeiReadOnlyVariable2PpiGuid, 0, NULL, (VOID **) &VariableServices ); ASSERT_EFI_ERROR (Status); VarSize = sizeof (EFI_PHYSICAL_ADDRESS); Status = VariableServices->GetVariable ( VariableServices, L"PerfDataMemAddr", &gPerformanceProtocolGuid, NULL, &VarSize, &mAcpiLowMemoryBase ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Fail to retrieve variable to log S3 performance data \n")); return; } PerfHeader = (PERF_HEADER *) (UINTN) mAcpiLowMemoryBase; if (PerfHeader->Signiture != PERFORMANCE_SIGNATURE) { DEBUG ((EFI_D_ERROR, "Performance data in ACPI memory get corrupted! \n")); return; } // // Record total S3 resume time. // if (EndValue >= StartValue) { PerfHeader->S3Resume = Ticker - StartValue; CountUp = TRUE; } else { PerfHeader->S3Resume = StartValue - Ticker; CountUp = FALSE; } // // Get S3 detailed performance data // Index = 0; LogEntryKey = 0; while ((LogEntryKey = GetPerformanceMeasurement ( LogEntryKey, &Handle, &Token, &Module, &StartTicker, &EndTicker)) != 0) { if (EndTicker != 0) { PerfData = &PerfHeader->S3Entry[Index]; // // Use File Handle to specify the different performance log for PEIM. // File Handle is the base address of PEIM FFS file. // if ((AsciiStrnCmp (Token, "PEIM", PEI_PERFORMANCE_STRING_SIZE) == 0) && (Handle != NULL)) { AsciiSPrint (PerfData->Token, PERF_TOKEN_LENGTH, "0x%11p", Handle); } else { AsciiStrnCpy (PerfData->Token, Token, PERF_TOKEN_LENGTH); } if (StartTicker == 1) { StartTicker = StartValue; } if (EndTicker == 1) { EndTicker = StartValue; } Ticker = CountUp? (EndTicker - StartTicker) : (StartTicker - EndTicker); PerfData->Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); // // Only Record > 1ms performance data so that more big performance can be recorded. // if ((Ticker > Freq) && (++Index >= PERF_PEI_ENTRY_MAX_NUM)) { // // Reach the maximum number of PEI performance log entries. // break; } } } PerfHeader->S3EntryNum = (UINT32) Index; }