From d41bc92c99edd0e22dd60b5a415539987a631234 Mon Sep 17 00:00:00 2001 From: jcarsey Date: Mon, 4 Mar 2013 22:02:59 +0000 Subject: [PATCH] ShellPkg: Add "dp" command library to ShellPkg. This command is only included in the build with command line option "-D INCLUDE_DP". The user must also update the DSC with appropriate library instances that match the platform for the build to succeed. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jaben Carsey Reviewed-by: Erik Bjorge git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@14160 6f19259b-4bc3-4df7-8a09-765794883524 --- ShellPkg/Library/UefiDpLib/Dp.c | 310 +++++++++ ShellPkg/Library/UefiDpLib/Dp.h | 98 +++ ShellPkg/Library/UefiDpLib/DpInternal.h | 340 ++++++++++ ShellPkg/Library/UefiDpLib/DpProfile.c | 101 +++ ShellPkg/Library/UefiDpLib/DpTrace.c | 828 +++++++++++++++++++++++ ShellPkg/Library/UefiDpLib/DpUtilities.c | 400 +++++++++++ ShellPkg/Library/UefiDpLib/Literals.c | 23 + ShellPkg/Library/UefiDpLib/Literals.h | 26 + ShellPkg/Library/UefiDpLib/UefiDpLib.c | 101 +++ ShellPkg/Library/UefiDpLib/UefiDpLib.h | 60 ++ ShellPkg/Library/UefiDpLib/UefiDpLib.inf | 75 ++ ShellPkg/Library/UefiDpLib/UefiDpLib.uni | Bin 0 -> 17140 bytes ShellPkg/Library/UefiDpLib/readme.txt | 2 + ShellPkg/ShellPkg.dsc | 16 +- 14 files changed, 2377 insertions(+), 3 deletions(-) create mode 100644 ShellPkg/Library/UefiDpLib/Dp.c create mode 100644 ShellPkg/Library/UefiDpLib/Dp.h create mode 100644 ShellPkg/Library/UefiDpLib/DpInternal.h create mode 100644 ShellPkg/Library/UefiDpLib/DpProfile.c create mode 100644 ShellPkg/Library/UefiDpLib/DpTrace.c create mode 100644 ShellPkg/Library/UefiDpLib/DpUtilities.c create mode 100644 ShellPkg/Library/UefiDpLib/Literals.c create mode 100644 ShellPkg/Library/UefiDpLib/Literals.h create mode 100644 ShellPkg/Library/UefiDpLib/UefiDpLib.c create mode 100644 ShellPkg/Library/UefiDpLib/UefiDpLib.h create mode 100644 ShellPkg/Library/UefiDpLib/UefiDpLib.inf create mode 100644 ShellPkg/Library/UefiDpLib/UefiDpLib.uni create mode 100644 ShellPkg/Library/UefiDpLib/readme.txt diff --git a/ShellPkg/Library/UefiDpLib/Dp.c b/ShellPkg/Library/UefiDpLib/Dp.c new file mode 100644 index 0000000000..b5a6e7a713 --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/Dp.c @@ -0,0 +1,310 @@ +/** @file + Shell command for Displaying Performance Metrics. + + The Dp command reads performance data and presents it in several + different formats depending upon the needs of the user. Both + Trace and Measured Profiling information is processed and presented. + + Dp uses the "PerformanceLib" to read the measurement records. + The "TimerLib" provides information about the timer, such as frequency, + beginning, and ending counter values. + Measurement records contain identifying information (Handle, Token, Module) + and start and end time values. + Dp uses this information to group records in different ways. It also uses + timer information to calculate elapsed time for each measurement. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include "UefiDpLib.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "Dp.h" +#include "Literals.h" +#include "DpInternal.h" + +// +/// Module-Global Variables +///@{ +CHAR16 mGaugeString[DP_GAUGE_STRING_LENGTH + 1]; +CHAR16 mUnicodeToken[DXE_PERFORMANCE_STRING_SIZE]; +UINT64 mInterestThreshold; +BOOLEAN mShowId = FALSE; + +PERF_SUMMARY_DATA SummaryData = { 0 }; ///< Create the SummaryData structure and init. to ZERO. + +/// Timer Specific Information. +TIMER_INFO TimerInfo; + +/// Items for which to gather cumulative statistics. +PERF_CUM_DATA CumData[] = { + PERF_INIT_CUM_DATA (LOAD_IMAGE_TOK), + PERF_INIT_CUM_DATA (START_IMAGE_TOK), + PERF_INIT_CUM_DATA (DRIVERBINDING_START_TOK), + PERF_INIT_CUM_DATA (DRIVERBINDING_SUPPORT_TOK) +}; + +/// Number of items for which we are gathering cumulative statistics. +UINT32 const NumCum = sizeof(CumData) / sizeof(PERF_CUM_DATA); + +STATIC CONST SHELL_PARAM_ITEM ParamList[] = { + {L"-v", TypeFlag}, // -v Verbose Mode + {L"-A", TypeFlag}, // -A All, Cooked + {L"-R", TypeFlag}, // -R RAW All + {L"-s", TypeFlag}, // -s Summary +#if PROFILING_IMPLEMENTED + {L"-P", TypeFlag}, // -P Dump Profile Data + {L"-T", TypeFlag}, // -T Dump Trace Data +#endif // PROFILING_IMPLEMENTED + {L"-x", TypeFlag}, // -x eXclude Cumulative Items + {L"-i", TypeFlag}, // -i Display Identifier + {L"-n", TypeValue}, // -n # Number of records to display for A and R + {L"-t", TypeValue}, // -t # Threshold of interest + {NULL, TypeMax} + }; + +///@} + +/** + Display the trailing Verbose information. +**/ +VOID +DumpStatistics( void ) +{ + EFI_STRING StringPtr; + EFI_STRING StringPtrUnknown; + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_STATISTICS), NULL); + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown : StringPtr); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMTRACE), gDpHiiHandle, SummaryData.NumTrace); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMINCOMPLETE), gDpHiiHandle, SummaryData.NumIncomplete); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMPHASES), gDpHiiHandle, SummaryData.NumSummary); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMHANDLES), gDpHiiHandle, SummaryData.NumHandles, SummaryData.NumTrace - SummaryData.NumHandles); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMPEIMS), gDpHiiHandle, SummaryData.NumPEIMs); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMGLOBALS), gDpHiiHandle, SummaryData.NumGlobal); +#if PROFILING_IMPLEMENTED + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_STATS_NUMPROFILE), gDpHiiHandle, SummaryData.NumProfile); +#endif // PROFILING_IMPLEMENTED + SHELL_FREE_NON_NULL (StringPtr); + SHELL_FREE_NON_NULL (StringPtrUnknown); +} + +/** + Dump performance data. + + @param[in] ImageHandle The image handle. + @param[in] SystemTable The system table. + + @retval EFI_SUCCESS Command completed successfully. + @retval EFI_INVALID_PARAMETER Command usage error. + @retval value Unknown error. + +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunDp ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + LIST_ENTRY *ParamPackage; + CONST CHAR16 *CmdLineArg; + EFI_STATUS Status; + + UINT64 Freq; + UINT64 Ticker; + UINTN Number2Display; + + EFI_STRING StringPtr = NULL; + BOOLEAN SummaryMode = FALSE; + BOOLEAN VerboseMode = FALSE; + BOOLEAN AllMode = FALSE; + BOOLEAN RawMode = FALSE; + BOOLEAN TraceMode = FALSE; + BOOLEAN ProfileMode = FALSE; + BOOLEAN ExcludeMode = FALSE; + + // Get DP's entry time as soon as possible. + // This is used as the Shell-Phase end time. + // + Ticker = GetPerformanceCounter (); + + // + // initialize the shell lib (we must be in non-auto-init...) + // + Status = ShellInitialize(); + ASSERT_EFI_ERROR(Status); + + Status = CommandInit(); + ASSERT_EFI_ERROR(Status); + + // + // Process Command Line arguments + // + Status = ShellCommandLineParse (ParamList, &ParamPackage, NULL, TRUE); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_INVALID_ARG), gDpHiiHandle); + return SHELL_INVALID_PARAMETER; + } + + // + // Boolean options + // + VerboseMode = ShellCommandLineGetFlag (ParamPackage, L"-v"); + SummaryMode = (BOOLEAN) (ShellCommandLineGetFlag (ParamPackage, L"-S") || ShellCommandLineGetFlag (ParamPackage, L"-s")); + AllMode = ShellCommandLineGetFlag (ParamPackage, L"-A"); + RawMode = ShellCommandLineGetFlag (ParamPackage, L"-R"); +#if PROFILING_IMPLEMENTED + TraceMode = ShellCommandLineGetFlag (ParamPackage, L"-T"); + ProfileMode = ShellCommandLineGetFlag (ParamPackage, L"-P"); +#endif // PROFILING_IMPLEMENTED + ExcludeMode = ShellCommandLineGetFlag (ParamPackage, L"-x"); + mShowId = ShellCommandLineGetFlag (ParamPackage, L"-i"); + + // Options with Values + CmdLineArg = ShellCommandLineGetValue (ParamPackage, L"-n"); + if (CmdLineArg == NULL) { + Number2Display = DEFAULT_DISPLAYCOUNT; + } else { + Number2Display = StrDecimalToUintn(CmdLineArg); + if (Number2Display == 0) { + Number2Display = MAXIMUM_DISPLAYCOUNT; + } + } + + CmdLineArg = ShellCommandLineGetValue (ParamPackage, L"-t"); + if (CmdLineArg == NULL) { + mInterestThreshold = DEFAULT_THRESHOLD; // 1ms := 1,000 us + } else { + mInterestThreshold = StrDecimalToUint64(CmdLineArg); + } + + // Handle Flag combinations and default behaviors + // If both TraceMode and ProfileMode are FALSE, set them both to TRUE + if ((! TraceMode) && (! ProfileMode)) { + TraceMode = TRUE; +#if PROFILING_IMPLEMENTED + ProfileMode = TRUE; +#endif // PROFILING_IMPLEMENTED + } + + // + // Timer specific processing + // + // Get the Performance counter characteristics: + // Freq = Frequency in Hz + // StartCount = Value loaded into the counter when it starts counting + // EndCount = Value counter counts to before it needs to be reset + // + Freq = GetPerformanceCounterProperties (&TimerInfo.StartCount, &TimerInfo.EndCount); + + // Convert the Frequency from Hz to KHz + TimerInfo.Frequency = (UINT32)DivU64x32 (Freq, 1000); + + // Determine in which direction the performance counter counts. + TimerInfo.CountUp = (BOOLEAN) (TimerInfo.EndCount >= TimerInfo.StartCount); + + // + // Print header + // + // print DP's build version + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_BUILD_REVISION), gDpHiiHandle, DP_MAJOR_VERSION, DP_MINOR_VERSION); + + // print performance timer characteristics + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_KHZ), gDpHiiHandle, TimerInfo.Frequency); + + if (VerboseMode && !RawMode) { + StringPtr = HiiGetString (gDpHiiHandle, + (EFI_STRING_ID) (TimerInfo.CountUp ? STRING_TOKEN (STR_DP_UP) : STRING_TOKEN (STR_DP_DOWN)), NULL); + ASSERT (StringPtr != NULL); + // Print Timer count range and direction + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_TIMER_PROPERTIES), gDpHiiHandle, + StringPtr, + TimerInfo.StartCount, + TimerInfo.EndCount + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_VERBOSE_THRESHOLD), gDpHiiHandle, mInterestThreshold); + } + +/**************************************************************************** +**** Print Sections based on command line options +**** +**** Option modes have the following priority: +**** v Verbose -- Valid in combination with any other options +**** t Threshold -- Modifies All, Raw, and Cooked output +**** Default is 0 for All and Raw mode +**** Default is DEFAULT_THRESHOLD for "Cooked" mode +**** n Number2Display Used by All and Raw mode. Otherwise ignored. +**** A All -- R and S options are ignored +**** R Raw -- S option is ignored +**** s Summary -- Modifies "Cooked" output only +**** Cooked (Default) +**** +**** The All, Raw, and Cooked modes are modified by the Trace and Profile +**** options. +**** !T && !P := (0) Default, Both are displayed +**** T && !P := (1) Only Trace records are displayed +**** !T && P := (2) Only Profile records are displayed +**** T && P := (3) Same as Default, both are displayed +****************************************************************************/ + GatherStatistics(); + if (AllMode) { + if (TraceMode) { + DumpAllTrace( Number2Display, ExcludeMode); + } + if (ProfileMode) { + DumpAllProfile( Number2Display, ExcludeMode); + } + } else if (RawMode) { + if (TraceMode) { + DumpRawTrace( Number2Display, ExcludeMode); + } + if (ProfileMode) { + DumpRawProfile( Number2Display, ExcludeMode); + } + } else { + //------------- Begin Cooked Mode Processing + if (TraceMode) { + ProcessPhases ( Ticker ); + if ( ! SummaryMode) { + Status = ProcessHandles ( ExcludeMode); + if ( ! EFI_ERROR( Status)) { + ProcessPeims (); + ProcessGlobal (); + ProcessCumulative (); + } + } + } + if (ProfileMode) { + DumpAllProfile( Number2Display, ExcludeMode); + } + } //------------- End of Cooked Mode Processing + if ( VerboseMode || SummaryMode) { + DumpStatistics(); + } + + SHELL_FREE_NON_NULL (StringPtr); + + return SHELL_SUCCESS; +} diff --git a/ShellPkg/Library/UefiDpLib/Dp.h b/ShellPkg/Library/UefiDpLib/Dp.h new file mode 100644 index 0000000000..72e2c3ba18 --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/Dp.h @@ -0,0 +1,98 @@ +/** @file + Common declarations for the Dp Performance Reporting Utility. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#ifndef _EFI_APP_DP_H_ +#define _EFI_APP_DP_H_ + +#include +#include + +#define DP_MAJOR_VERSION 2 +#define DP_MINOR_VERSION 4 + +/** + * The value assigned to DP_DEBUG controls which debug output + * is generated. Set it to ZERO to disable. +**/ +#define DP_DEBUG 0 + +/** + * Set to 1 once Profiling has been implemented in order to enable + * profiling related options and report output. +**/ +#define PROFILING_IMPLEMENTED 0 + +#define DEFAULT_THRESHOLD 1000 ///< One millisecond. +#define DEFAULT_DISPLAYCOUNT 50 +#define MAXIMUM_DISPLAYCOUNT 999999 ///< Arbitrary maximum reasonable number. + +#define PERF_MAXDUR 0xFFFFFFFFFFFFFFFFULL + +/// Determine whether 0 <= C < L. If L == 0, return true regardless of C. +#define WITHIN_LIMIT( C, L) ( ((L) == 0) || ((C) < (L)) ) + +/// Structure for storing Timer specific information. +typedef struct { + UINT64 StartCount; ///< Value timer is initialized with. + UINT64 EndCount; ///< Value timer has just before it wraps. + UINT32 Frequency; ///< Timer count frequency in KHz. + BOOLEAN CountUp; ///< TRUE if the counter counts up. +} TIMER_INFO; + +/** Initialize one PERF_CUM_DATA structure instance for token t. + * + * This parameterized macro takes a single argument, t, which is expected + * to resolve to a pointer to an ASCII string literal. This parameter may + * take any one of the following forms: + * - PERF_INIT_CUM_DATA("Token") A string literal + * - PERF_INIT_CUM_DATA(pointer) A pointer -- CHAR8 *pointer; + * - PERF_INIT_CUM_DATA(array) Address of an array -- CHAR8 array[N]; +**/ +#define PERF_INIT_CUM_DATA(t) { 0ULL, PERF_MAXDUR, 0ULL, (t), 0U } + +typedef struct { + UINT64 Duration; ///< Cumulative duration for this item. + UINT64 MinDur; ///< Smallest duration encountered. + UINT64 MaxDur; ///< Largest duration encountered. + CHAR8 *Name; ///< ASCII name of this item. + UINT32 Count; ///< Total number of measurements accumulated. +} PERF_CUM_DATA; + +typedef struct { + UINT32 NumTrace; ///< Number of recorded TRACE performance measurements. + UINT32 NumProfile; ///< Number of recorded PROFILE performance measurements. + UINT32 NumIncomplete; ///< Number of measurements with no END value. + UINT32 NumSummary; ///< Number of summary section measurements. + UINT32 NumHandles; ///< Number of measurements with handles. + UINT32 NumPEIMs; ///< Number of measurements of PEIMs. + UINT32 NumGlobal; ///< Number of measurements with END value and NULL handle. +} PERF_SUMMARY_DATA; + +typedef struct { + CONST VOID *Handle; + CONST CHAR8 *Token; ///< Measured token string name. + CONST CHAR8 *Module; ///< Module string name. + UINT64 StartTimeStamp; ///< Start time point. + UINT64 EndTimeStamp; ///< End time point. + UINT32 Identifier; ///< Identifier. +} MEASUREMENT_RECORD; + +typedef struct { + CHAR8 *Name; ///< Measured token string name. + UINT64 CumulativeTime; ///< Accumulated Elapsed Time. + UINT64 MinTime; ///< Minimum Elapsed Time. + UINT64 MaxTime; ///< Maximum Elapsed Time. + UINT32 Count; ///< Number of measurements accumulated. +} PROFILE_RECORD; + +#endif // _EFI_APP_DP_H_ diff --git a/ShellPkg/Library/UefiDpLib/DpInternal.h b/ShellPkg/Library/UefiDpLib/DpInternal.h new file mode 100644 index 0000000000..c7d9a592fe --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/DpInternal.h @@ -0,0 +1,340 @@ +/** @file + Declarations of objects defined internally to the Dp Application. + + Declarations of data and functions which are private to the Dp application. + This file should never be referenced by anything other than components of the + Dp application. In addition to global data, function declarations for + DpUtilities.c, DpTrace.c, and DpProfile.c are included here. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ +#ifndef _DP_INTELNAL_H_ +#define _DP_INTELNAL_H_ + +#define DP_GAUGE_STRING_LENGTH 36 + +// +/// Module-Global Variables +///@{ +extern EFI_HII_HANDLE gDpHiiHandle; +extern CHAR16 mGaugeString[DP_GAUGE_STRING_LENGTH + 1]; +extern CHAR16 mUnicodeToken[DXE_PERFORMANCE_STRING_SIZE]; +extern UINT64 mInterestThreshold; +extern BOOLEAN mShowId; + +extern PERF_SUMMARY_DATA SummaryData; ///< Create the SummaryData structure and init. to ZERO. + +/// Timer Specific Information. +extern TIMER_INFO TimerInfo; + +/// Items for which to gather cumulative statistics. +extern PERF_CUM_DATA CumData[]; + +/// Number of items for which we are gathering cumulative statistics. +extern UINT32 const NumCum; + +///@} + +/** + Calculate an event's duration in timer ticks. + + Given the count direction and the event's start and end timer values, + calculate the duration of the event in timer ticks. Information for + the current measurement is pointed to by the parameter. + + If the measurement's start time is 1, it indicates that the developer + is indicating that the measurement began at the release of reset. + The start time is adjusted to the timer's starting count before performing + the elapsed time calculation. + + The calculated duration, in ticks, is the absolute difference between + the measurement's ending and starting counts. + + @param Measurement Pointer to a MEASUREMENT_RECORD structure containing + data for the current measurement. + + @return The 64-bit duration of the event. +**/ +UINT64 +GetDuration ( + IN OUT MEASUREMENT_RECORD *Measurement + ); + +/** + Determine whether the Measurement record is for an EFI Phase. + + The Token and Module members of the measurement record are checked. + Module must be empty and Token must be one of SEC, PEI, DXE, BDS, or SHELL. + + @param[in] Measurement A pointer to the Measurement record to test. + + @retval TRUE The measurement record is for an EFI Phase. + @retval FALSE The measurement record is NOT for an EFI Phase. +**/ +BOOLEAN +IsPhase( + IN MEASUREMENT_RECORD *Measurement + ); + +/** + Get the file name portion of the Pdb File Name. + + The portion of the Pdb File Name between the last backslash and + either a following period or the end of the string is converted + to Unicode and copied into UnicodeBuffer. The name is truncated, + if necessary, to ensure that UnicodeBuffer is not overrun. + + @param[in] PdbFileName Pdb file name. + @param[out] UnicodeBuffer The resultant Unicode File Name. + +**/ +VOID +GetShortPdbFileName ( + IN CHAR8 *PdbFileName, + OUT CHAR16 *UnicodeBuffer + ); + +/** + Get a human readable name for an image handle. + The following methods will be tried orderly: + 1. Image PDB + 2. ComponentName2 protocol + 3. FFS UI section + 4. Image GUID + 5. Image DevicePath + 6. Unknown Driver Name + + @param[in] Handle + + @post The resulting Unicode name string is stored in the + mGaugeString global array. + +**/ +VOID +GetNameFromHandle ( + IN EFI_HANDLE Handle + ); + +/** + Calculate the Duration in microseconds. + + Duration is multiplied by 1000, instead of Frequency being divided by 1000 or + multiplying the result by 1000, in order to maintain precision. Since Duration is + a 64-bit value, multiplying it by 1000 is unlikely to produce an overflow. + + The time is calculated as (Duration * 1000) / Timer_Frequency. + + @param[in] Duration The event duration in timer ticks. + + @return A 64-bit value which is the Elapsed time in microseconds. +**/ +UINT64 +DurationInMicroSeconds ( + IN UINT64 Duration + ); + +/** + Get index of Measurement Record's match in the CumData array. + + If the Measurement's Token value matches a Token in one of the CumData + records, the index of the matching record is returned. The returned + index is a signed value so that negative values can indicate that + the Measurement didn't match any entry in the CumData array. + + @param[in] Measurement A pointer to a Measurement Record to match against the CumData array. + + @retval <0 Token is not in the CumData array. + @retval >=0 Return value is the index into CumData where Token is found. +**/ +INTN +GetCumulativeItem( + IN MEASUREMENT_RECORD *Measurement + ); + +/** + Collect verbose statistics about the logged performance measurements. + + General Summary information for all Trace measurements is gathered and + stored within the SummaryData structure. This information is both + used internally by subsequent reporting functions, and displayed + at the end of verbose reports. + + @pre The SummaryData and CumData structures must be initialized + prior to calling this function. + + @post The SummaryData and CumData structures contain statistics for the + current performance logs. +**/ +VOID +GatherStatistics( + VOID + ); + +/** + Gather and print ALL Trace Records. + + Displays all "interesting" Trace measurements in order.
+ The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + The mGaugeString and mUnicodeToken global arrays are used for temporary string storage. + They must not be in use by a calling function. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpAllTrace( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ); + +/** + Gather and print Raw Trace Records. + + All Trace measurements with a duration greater than or equal to + mInterestThreshold are printed without interpretation. + + The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpRawTrace( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ); + +/** + Gather and print Major Phase metrics. + + @param[in] Ticker The timer value for the END of Shell phase + +**/ +VOID +ProcessPhases( + IN UINT64 Ticker + ); + + +/** + Gather and print Handle data. + + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + + @return Status from a call to gBS->LocateHandle(). +**/ +EFI_STATUS +ProcessHandles( + IN BOOLEAN ExcludeFlag + ); + + +/** + Gather and print PEIM data. + + Only prints complete PEIM records + +**/ +VOID +ProcessPeims( + VOID + ); + +/** + Gather and print global data. + + Strips out incomplete or "Execution Phase" records + Only prints records where Handle is NULL + Increment TIndex for every record, even skipped ones, so that we have an + indication of every measurement record taken. + +**/ +VOID +ProcessGlobal( + VOID + ); + +/** + Gather and print cumulative data. + + Traverse the measurement records and:
+ For each record with a Token listed in the CumData array:
+ - Update the instance count and the total, minimum, and maximum durations. + Finally, print the gathered cumulative statistics. + +**/ +VOID +ProcessCumulative( + VOID + ); + +/** + Gather and print ALL Profiling Records. + + Displays all "interesting" Profile measurements in order. + The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + The mGaugeString and mUnicodeToken global arrays are used for temporary string storage. + They must not be in use by a calling function. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpAllProfile( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ); + +/** + Gather and print Raw Profile Records. + + All Profile measurements with a duration greater than or equal to + mInterestThreshold are printed without interpretation. + + The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpRawProfile( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ); + +#endif diff --git a/ShellPkg/Library/UefiDpLib/DpProfile.c b/ShellPkg/Library/UefiDpLib/DpProfile.c new file mode 100644 index 0000000000..64583988c1 --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/DpProfile.c @@ -0,0 +1,101 @@ +/** @file + Measured Profiling reporting for the Dp utility. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Dp.h" +#include "Literals.h" +#include "DpInternal.h" + +/** + Gather and print ALL Profiling Records. + + Displays all "interesting" Profile measurements in order. + The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + The mGaugeString and mUnicodeToken global arrays are used for temporary string storage. + They must not be in use by a calling function. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpAllProfile( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ) +{ + EFI_STRING StringPtr; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_PROFILE), NULL); + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown: StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); +} + +/** + Gather and print Raw Profile Records. + + All Profile measurements with a duration greater than or equal to + mInterestThreshold are printed without interpretation. + + The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpRawProfile( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ) +{ + EFI_STRING StringPtr; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_RAWPROFILE), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown: StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); +} diff --git a/ShellPkg/Library/UefiDpLib/DpTrace.c b/ShellPkg/Library/UefiDpLib/DpTrace.c new file mode 100644 index 0000000000..de1d45aa8b --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/DpTrace.c @@ -0,0 +1,828 @@ +/** @file + Trace reporting for the Dp utility. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Dp.h" +#include "Literals.h" +#include "DpInternal.h" + +/** + Collect verbose statistics about the logged performance measurements. + + General Summary information for all Trace measurements is gathered and + stored within the SummaryData structure. This information is both + used internally by subsequent reporting functions, and displayed + at the end of verbose reports. + + @pre The SummaryData and CumData structures must be initialized + prior to calling this function. + + @post The SummaryData and CumData structures contain statistics for the + current performance logs. +**/ +VOID +GatherStatistics( + VOID +) +{ + MEASUREMENT_RECORD Measurement; + UINT64 Duration; + UINTN LogEntryKey; + INTN TIndex; + + LogEntryKey = 0; + while ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + { + ++SummaryData.NumTrace; // Count the number of TRACE Measurement records + if (Measurement.EndTimeStamp == 0) { + ++SummaryData.NumIncomplete; // Count the incomplete records + continue; + } + + if (Measurement.Handle != NULL) { + ++SummaryData.NumHandles; // Count the number of measurements with non-NULL handles + } + + if (IsPhase( &Measurement)) { + ++SummaryData.NumSummary; // Count the number of major phases + } + else { // !IsPhase(... + if(Measurement.Handle == NULL) { + ++SummaryData.NumGlobal; + } + } + + if (AsciiStrnCmp (Measurement.Token, ALit_PEIM, PERF_TOKEN_LENGTH) == 0) { + ++SummaryData.NumPEIMs; // Count PEIM measurements + } + + Duration = GetDuration (&Measurement); + TIndex = GetCumulativeItem (&Measurement); + if (TIndex >= 0) { + CumData[TIndex].Duration += Duration; + CumData[TIndex].Count++; + if ( Duration < CumData[TIndex].MinDur ) { + CumData[TIndex].MinDur = Duration; + } + if ( Duration > CumData[TIndex].MaxDur ) { + CumData[TIndex].MaxDur = Duration; + } + } + } +} + +/** + Gather and print ALL Trace Records. + + Displays all "interesting" Trace measurements in order.
+ The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + The mGaugeString and mUnicodeToken global arrays are used for temporary string storage. + They must not be in use by a calling function. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpAllTrace( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ) +{ + MEASUREMENT_RECORD Measurement; + UINT64 ElapsedTime; + UINT64 Duration; + CHAR16 *IncFlag; + UINTN LogEntryKey; + UINTN Count; + UINTN Index; + UINTN TIndex; + + EFI_HANDLE *HandleBuffer; + UINTN Size; + EFI_HANDLE TempHandle; + EFI_STATUS Status; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + IncFlag = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_ALL), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (IncFlag == NULL) ? StringPtrUnknown : IncFlag); + FreePool (StringPtrUnknown); + + // Get Handle information + // + Size = 0; + HandleBuffer = &TempHandle; + Status = gBS->LocateHandle (AllHandles, NULL, NULL, &Size, &TempHandle); + if (Status == EFI_BUFFER_TOO_SMALL) { + HandleBuffer = AllocatePool (Size); + ASSERT (HandleBuffer != NULL); + if (HandleBuffer == NULL) { + return; + } + Status = gBS->LocateHandle (AllHandles, NULL, NULL, &Size, HandleBuffer); + } + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_HANDLES_ERROR), gDpHiiHandle, Status); + } + else { + // We have successfully populated the HandleBuffer + // Display ALL Measurement Records + // Up to Limit lines displayed + // Display only records with Elapsed times >= mInterestThreshold + // Display driver names in Module field for records with Handles. + // + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_ALL_HEADR2), gDpHiiHandle); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_ALL_DASHES2), gDpHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_ALL_HEADR), gDpHiiHandle); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_DASHES), gDpHiiHandle); + } + + LogEntryKey = 0; + Count = 0; + Index = 0; + while ( WITHIN_LIMIT(Count, Limit) && + ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + ) + { + ++Index; // Count every record. First record is 1. + ElapsedTime = 0; + SHELL_FREE_NON_NULL (IncFlag); + if (Measurement.EndTimeStamp != 0) { + Duration = GetDuration (&Measurement); + ElapsedTime = DurationInMicroSeconds ( Duration ); + IncFlag = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_COMPLETE), NULL); + } + else { + IncFlag = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_INCOMPLETE), NULL); // Mark incomplete records + } + if (((Measurement.EndTimeStamp != 0) && (ElapsedTime < mInterestThreshold)) || + ((ExcludeFlag) && (GetCumulativeItem(&Measurement) >= 0)) + ) { // Ignore "uninteresting" or excluded records + continue; + } + ++Count; // Count the number of records printed + + // If Handle is non-zero, see if we can determine a name for the driver + AsciiStrToUnicodeStr (Measurement.Module, mGaugeString); // Use Module by default + AsciiStrToUnicodeStr (Measurement.Token, mUnicodeToken); + if (Measurement.Handle != NULL) { + // See if the Handle is in the HandleBuffer + for (TIndex = 0; TIndex < (Size / sizeof(HandleBuffer[0])); TIndex++) { + if (Measurement.Handle == HandleBuffer[TIndex]) { + GetNameFromHandle (HandleBuffer[TIndex]); + break; + } + } + } + + if (AsciiStrnCmp (Measurement.Token, ALit_PEIM, PERF_TOKEN_LENGTH) == 0) { + UnicodeSPrint (mGaugeString, sizeof (mGaugeString), L"%g", Measurement.Handle); + } + + // Ensure that the argument strings are not too long. + mGaugeString[DP_GAUGE_STRING_LENGTH] = 0; + mUnicodeToken[13] = 0; + + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_ALL_VARS2), gDpHiiHandle, + Index, // 1 based, Which measurement record is being printed + IncFlag, + Measurement.Handle, + mGaugeString, + mUnicodeToken, + ElapsedTime, + Measurement.Identifier + ); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_ALL_VARS), gDpHiiHandle, + Index, // 1 based, Which measurement record is being printed + IncFlag, + Measurement.Handle, + mGaugeString, + mUnicodeToken, + ElapsedTime + ); + } + } + } + if (HandleBuffer != &TempHandle) { + FreePool (HandleBuffer); + } + SHELL_FREE_NON_NULL (IncFlag); +} + +/** + Gather and print Raw Trace Records. + + All Trace measurements with a duration greater than or equal to + mInterestThreshold are printed without interpretation. + + The number of records displayed is controlled by: + - records with a duration less than mInterestThreshold microseconds are not displayed. + - No more than Limit records are displayed. A Limit of zero will not limit the output. + - If the ExcludeFlag is TRUE, records matching entries in the CumData array are not + displayed. + + @pre The mInterestThreshold global variable is set to the shortest duration to be printed. + + @param[in] Limit The number of records to print. Zero is ALL. + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + +**/ +VOID +DumpRawTrace( + IN UINTN Limit, + IN BOOLEAN ExcludeFlag + ) +{ + MEASUREMENT_RECORD Measurement; + UINT64 ElapsedTime; + UINT64 Duration; + UINTN LogEntryKey; + UINTN Count; + UINTN Index; + + EFI_STRING StringPtr; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_RAWTRACE), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown : StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); + + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_RAW_HEADR2), gDpHiiHandle); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_RAW_DASHES2), gDpHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_RAW_HEADR), gDpHiiHandle); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_RAW_DASHES), gDpHiiHandle); + } + + LogEntryKey = 0; + Count = 0; + Index = 0; + while ( WITHIN_LIMIT(Count, Limit) && + ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + ) + { + ++Index; // Count every record. First record is 1. + ElapsedTime = 0; + if (Measurement.EndTimeStamp != 0) { + Duration = GetDuration (&Measurement); + ElapsedTime = DurationInMicroSeconds ( Duration ); + } + if ((ElapsedTime < mInterestThreshold) || + ((ExcludeFlag) && (GetCumulativeItem(&Measurement) >= 0)) + ) { // Ignore "uninteresting" or Excluded records + continue; + } + ++Count; // Count the number of records printed + + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_RAW_VARS2), gDpHiiHandle, + Index, // 1 based, Which measurement record is being printed + Measurement.Handle, + Measurement.StartTimeStamp, + Measurement.EndTimeStamp, + Measurement.Token, + Measurement.Module, + Measurement.Identifier + ); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_RAW_VARS), gDpHiiHandle, + Index, // 1 based, Which measurement record is being printed + Measurement.Handle, + Measurement.StartTimeStamp, + Measurement.EndTimeStamp, + Measurement.Token, + Measurement.Module + ); + } + } +} + +/** + Gather and print Major Phase metrics. + + @param[in] Ticker The timer value for the END of Shell phase + +**/ +VOID +ProcessPhases( + IN UINT64 Ticker + ) +{ + MEASUREMENT_RECORD Measurement; + UINT64 BdsTimeoutValue; + UINT64 SecTime; + UINT64 PeiTime; + UINT64 DxeTime; + UINT64 BdsTime; + UINT64 ShellTime; + UINT64 ElapsedTime; + UINT64 Duration; + UINT64 Total; + EFI_STRING StringPtr; + UINTN LogEntryKey; + EFI_STRING StringPtrUnknown; + + BdsTimeoutValue = 0; + SecTime = 0; + PeiTime = 0; + DxeTime = 0; + BdsTime = 0; + ShellTime = 0; + // + // Get Execution Phase Statistics + // + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_PHASES), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown : StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); + + LogEntryKey = 0; + while ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + { + if (AsciiStrnCmp (Measurement.Token, ALit_SHELL, PERF_TOKEN_LENGTH) == 0) { + Measurement.EndTimeStamp = Ticker; + } + if (Measurement.EndTimeStamp == 0) { // Skip "incomplete" records + continue; + } + Duration = GetDuration (&Measurement); + if ( Measurement.Handle != NULL + && (AsciiStrnCmp (Measurement.Token, ALit_BdsTO, PERF_TOKEN_LENGTH) == 0) + ) + { + BdsTimeoutValue = Duration; + } else if (AsciiStrnCmp (Measurement.Token, ALit_SEC, PERF_TOKEN_LENGTH) == 0) { + SecTime = Duration; + } else if (AsciiStrnCmp (Measurement.Token, ALit_PEI, PERF_TOKEN_LENGTH) == 0) { + PeiTime = Duration; + } else if (AsciiStrnCmp (Measurement.Token, ALit_DXE, PERF_TOKEN_LENGTH) == 0) { + DxeTime = Duration; + } else if (AsciiStrnCmp (Measurement.Token, ALit_BDS, PERF_TOKEN_LENGTH) == 0) { + BdsTime = Duration; + } else if (AsciiStrnCmp (Measurement.Token, ALit_SHELL, PERF_TOKEN_LENGTH) == 0) { + ShellTime = Duration; + } + } + + Total = 0; + + // print SEC phase duration time + // + if (SecTime > 0) { + ElapsedTime = DurationInMicroSeconds ( SecTime ); // Calculate elapsed time in microseconds + Total += DivU64x32 (ElapsedTime, 1000); // Accumulate time in milliseconds + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SEC_PHASE), gDpHiiHandle, ElapsedTime); + } + + // print PEI phase duration time + // + if (PeiTime > 0) { + ElapsedTime = DivU64x32 ( + PeiTime, + (UINT32)TimerInfo.Frequency + ); + Total += ElapsedTime; + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PHASE_DURATION), gDpHiiHandle, ALit_PEI, ElapsedTime); + } + + // print DXE phase duration time + // + if (DxeTime > 0) { + ElapsedTime = DivU64x32 ( + DxeTime, + (UINT32)TimerInfo.Frequency + ); + Total += ElapsedTime; + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PHASE_DURATION), gDpHiiHandle, ALit_DXE, ElapsedTime); + } + + // print BDS phase duration time + // + if (BdsTime > 0) { + ElapsedTime = DivU64x32 ( + BdsTime, + (UINT32)TimerInfo.Frequency + ); + Total += ElapsedTime; + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PHASE_DURATION), gDpHiiHandle, ALit_BDS, ElapsedTime); + } + + if (BdsTimeoutValue > 0) { + ElapsedTime = DivU64x32 ( + BdsTimeoutValue, + (UINT32)TimerInfo.Frequency + ); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PHASE_BDSTO), gDpHiiHandle, ALit_BdsTO, ElapsedTime); + } + + // print SHELL phase duration time + // + if (ShellTime > 0) { + ElapsedTime = DivU64x32 ( + ShellTime, + (UINT32)TimerInfo.Frequency + ); + Total += ElapsedTime; + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PHASE_DURATION), gDpHiiHandle, ALit_SHELL, ElapsedTime); + } + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_TOTAL_DURATION), gDpHiiHandle, Total); +} + +/** + Gather and print Handle data. + + @param[in] ExcludeFlag TRUE to exclude individual Cumulative items from display. + + @return Status from a call to gBS->LocateHandle(). +**/ +EFI_STATUS +ProcessHandles( + IN BOOLEAN ExcludeFlag + ) +{ + MEASUREMENT_RECORD Measurement; + UINT64 ElapsedTime; + UINT64 Duration; + EFI_HANDLE *HandleBuffer; + EFI_STRING StringPtr; + UINTN Index; + UINTN LogEntryKey; + UINTN Count; + UINTN Size; + EFI_HANDLE TempHandle; + EFI_STATUS Status; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_DRIVERS), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown : StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); + + Size = 0; + HandleBuffer = &TempHandle; + Status = gBS->LocateHandle (AllHandles, NULL, NULL, &Size, &TempHandle); + if (Status == EFI_BUFFER_TOO_SMALL) { + HandleBuffer = AllocatePool (Size); + ASSERT (HandleBuffer != NULL); + if (HandleBuffer == NULL) { + return Status; + } + Status = gBS->LocateHandle (AllHandles, NULL, NULL, &Size, HandleBuffer); + } + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_HANDLES_ERROR), gDpHiiHandle, Status); + } + else { +#if DP_DEBUG == 2 + Print (L"There are %,d Handles defined.\n", (Size / sizeof(HandleBuffer[0]))); +#endif + + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_HANDLE_SECTION2), gDpHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_HANDLE_SECTION), gDpHiiHandle); + } + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_DASHES), gDpHiiHandle); + + LogEntryKey = 0; + Count = 0; + while ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + { + Count++; + Duration = GetDuration (&Measurement); + ElapsedTime = DurationInMicroSeconds ( Duration ); + if ((ElapsedTime < mInterestThreshold) || + (Measurement.EndTimeStamp == 0) || + (Measurement.Handle == NULL) || + ((ExcludeFlag) && (GetCumulativeItem(&Measurement) >= 0)) + ) { // Ignore "uninteresting" or excluded records + continue; + } + mGaugeString[0] = 0; // Empty driver name by default + AsciiStrToUnicodeStr (Measurement.Token, mUnicodeToken); + // See if the Handle is in the HandleBuffer + for (Index = 0; Index < (Size / sizeof(HandleBuffer[0])); Index++) { + if (Measurement.Handle == HandleBuffer[Index]) { + GetNameFromHandle (HandleBuffer[Index]); // Name is put into mGaugeString + break; + } + } + // Ensure that the argument strings are not too long. + mGaugeString[DP_GAUGE_STRING_LENGTH] = 0; + mUnicodeToken[11] = 0; + if (mGaugeString[0] != 0) { + // Display the record if it has a valid handle. + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_HANDLE_VARS2), gDpHiiHandle, + Count, // 1 based, Which measurement record is being printed + Index + 1, // 1 based, Which handle is being printed + mGaugeString, + mUnicodeToken, + ElapsedTime, + Measurement.Identifier + ); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_HANDLE_VARS), gDpHiiHandle, + Count, // 1 based, Which measurement record is being printed + Index + 1, // 1 based, Which handle is being printed + mGaugeString, + mUnicodeToken, + ElapsedTime + ); + } + } + } + } + if (HandleBuffer != &TempHandle) { + FreePool (HandleBuffer); + } + return Status; +} + +/** + Gather and print PEIM data. + + Only prints complete PEIM records + +**/ +VOID +ProcessPeims( + VOID +) +{ + MEASUREMENT_RECORD Measurement; + UINT64 Duration; + UINT64 ElapsedTime; + EFI_STRING StringPtr; + UINTN LogEntryKey; + UINTN TIndex; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_PEIMS), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown : StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); + + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PEIM_SECTION2), gDpHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PEIM_SECTION), gDpHiiHandle); + } + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_DASHES), gDpHiiHandle); + TIndex = 0; + LogEntryKey = 0; + while ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + { + TIndex++; + if ((Measurement.EndTimeStamp == 0) || + (AsciiStrnCmp (Measurement.Token, ALit_PEIM, PERF_TOKEN_LENGTH) != 0) + ) { + continue; + } + + Duration = GetDuration (&Measurement); + ElapsedTime = DurationInMicroSeconds ( Duration ); // Calculate elapsed time in microseconds + if (ElapsedTime >= mInterestThreshold) { + // PEIM FILE Handle is the start address of its FFS file that contains its file guid. + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PEIM_VARS2), gDpHiiHandle, + TIndex, // 1 based, Which measurement record is being printed + Measurement.Handle, // base address + Measurement.Handle, // file guid + ElapsedTime, + Measurement.Identifier + ); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_PEIM_VARS), gDpHiiHandle, + TIndex, // 1 based, Which measurement record is being printed + Measurement.Handle, // base address + Measurement.Handle, // file guid + ElapsedTime + ); + } + } + } +} + +/** + Gather and print global data. + + Strips out incomplete or "Execution Phase" records + Only prints records where Handle is NULL + Increment TIndex for every record, even skipped ones, so that we have an + indication of every measurement record taken. + +**/ +VOID +ProcessGlobal( + VOID +) +{ + MEASUREMENT_RECORD Measurement; + UINT64 Duration; + UINT64 ElapsedTime; + EFI_STRING StringPtr; + UINTN LogEntryKey; + UINTN Index; // Index, or number, of the measurement record being processed + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_GENERAL), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown: StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); + + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_GLOBAL_SECTION2), gDpHiiHandle); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_GLOBAL_SECTION), gDpHiiHandle); + } + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_DASHES), gDpHiiHandle); + + Index = 1; + LogEntryKey = 0; + + while ((LogEntryKey = GetPerformanceMeasurementEx ( + LogEntryKey, + &Measurement.Handle, + &Measurement.Token, + &Measurement.Module, + &Measurement.StartTimeStamp, + &Measurement.EndTimeStamp, + &Measurement.Identifier)) != 0) + { + AsciiStrToUnicodeStr (Measurement.Module, mGaugeString); + AsciiStrToUnicodeStr (Measurement.Token, mUnicodeToken); + mGaugeString[25] = 0; + mUnicodeToken[31] = 0; + if ( ! ( IsPhase( &Measurement) || + (Measurement.Handle != NULL) || + (Measurement.EndTimeStamp == 0) + )) + { + Duration = GetDuration (&Measurement); + ElapsedTime = DurationInMicroSeconds ( Duration ); + if (ElapsedTime >= mInterestThreshold) { + if (mShowId) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_GLOBAL_VARS2), gDpHiiHandle, + Index, + mGaugeString, + mUnicodeToken, + ElapsedTime, + Measurement.Identifier + ); + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_GLOBAL_VARS), gDpHiiHandle, + Index, + mGaugeString, + mUnicodeToken, + ElapsedTime + ); + } + } + } + Index++; + } +} + +/** + Gather and print cumulative data. + + Traverse the measurement records and:
+ For each record with a Token listed in the CumData array:
+ - Update the instance count and the total, minimum, and maximum durations. + Finally, print the gathered cumulative statistics. + +**/ +VOID +ProcessCumulative( + VOID +) +{ + UINT64 AvgDur; // the computed average duration + UINT64 Dur; + UINT64 MinDur; + UINT64 MaxDur; + EFI_STRING StringPtr; + UINTN TIndex; + EFI_STRING StringPtrUnknown; + + StringPtrUnknown = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_ALIT_UNKNOWN), NULL); + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_SECTION_CUMULATIVE), NULL); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_SECTION_HEADER), gDpHiiHandle, + (StringPtr == NULL) ? StringPtrUnknown: StringPtr); + FreePool (StringPtr); + FreePool (StringPtrUnknown); + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_CUMULATIVE_SECT_1), gDpHiiHandle); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_CUMULATIVE_SECT_2), gDpHiiHandle); + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_DASHES), gDpHiiHandle); + + for ( TIndex = 0; TIndex < NumCum; ++TIndex) { + if (CumData[TIndex].Count != 0) { + AvgDur = DivU64x32 (CumData[TIndex].Duration, CumData[TIndex].Count); + AvgDur = DurationInMicroSeconds(AvgDur); + Dur = DurationInMicroSeconds(CumData[TIndex].Duration); + MaxDur = DurationInMicroSeconds(CumData[TIndex].MaxDur); + MinDur = DurationInMicroSeconds(CumData[TIndex].MinDur); + + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_DP_CUMULATIVE_STATS), gDpHiiHandle, + CumData[TIndex].Name, + CumData[TIndex].Count, + Dur, + AvgDur, + MinDur, + MaxDur + ); + } + } +} diff --git a/ShellPkg/Library/UefiDpLib/DpUtilities.c b/ShellPkg/Library/UefiDpLib/DpUtilities.c new file mode 100644 index 0000000000..89c9919e03 --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/DpUtilities.c @@ -0,0 +1,400 @@ +/** @file + Utility functions used by the Dp application. + + Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "Dp.h" +#include "Literals.h" +#include "DpInternal.h" + +/** + Calculate an event's duration in timer ticks. + + Given the count direction and the event's start and end timer values, + calculate the duration of the event in timer ticks. Information for + the current measurement is pointed to by the parameter. + + If the measurement's start time is 1, it indicates that the developer + is indicating that the measurement began at the release of reset. + The start time is adjusted to the timer's starting count before performing + the elapsed time calculation. + + The calculated duration, in ticks, is the absolute difference between + the measurement's ending and starting counts. + + @param Measurement Pointer to a MEASUREMENT_RECORD structure containing + data for the current measurement. + + @return The 64-bit duration of the event. +**/ +UINT64 +GetDuration ( + IN OUT MEASUREMENT_RECORD *Measurement + ) +{ + UINT64 Duration; + BOOLEAN Error; + + // PERF_START macros are called with a value of 1 to indicate + // the beginning of time. So, adjust the start ticker value + // to the real beginning of time. + // Assumes no wraparound. Even then, there is a very low probability + // of having a valid StartTicker value of 1. + if (Measurement->StartTimeStamp == 1) { + Measurement->StartTimeStamp = TimerInfo.StartCount; + } + if (TimerInfo.CountUp) { + Duration = Measurement->EndTimeStamp - Measurement->StartTimeStamp; + Error = (BOOLEAN)(Duration > Measurement->EndTimeStamp); + } + else { + Duration = Measurement->StartTimeStamp - Measurement->EndTimeStamp; + Error = (BOOLEAN)(Duration > Measurement->StartTimeStamp); + } + + if (Error) { + DEBUG ((EFI_D_ERROR, ALit_TimerLibError)); + Duration = 0; + } + return Duration; +} + +/** + Determine whether the Measurement record is for an EFI Phase. + + The Token and Module members of the measurement record are checked. + Module must be empty and Token must be one of SEC, PEI, DXE, BDS, or SHELL. + + @param[in] Measurement A pointer to the Measurement record to test. + + @retval TRUE The measurement record is for an EFI Phase. + @retval FALSE The measurement record is NOT for an EFI Phase. +**/ +BOOLEAN +IsPhase( + IN MEASUREMENT_RECORD *Measurement + ) +{ + BOOLEAN RetVal; + + RetVal = (BOOLEAN)( ( *Measurement->Module == '\0') && + ((AsciiStrnCmp (Measurement->Token, ALit_SEC, PERF_TOKEN_LENGTH) == 0) || + (AsciiStrnCmp (Measurement->Token, ALit_PEI, PERF_TOKEN_LENGTH) == 0) || + (AsciiStrnCmp (Measurement->Token, ALit_DXE, PERF_TOKEN_LENGTH) == 0) || + (AsciiStrnCmp (Measurement->Token, ALit_BDS, PERF_TOKEN_LENGTH) == 0)) + ); + return RetVal; +} + +/** + Get the file name portion of the Pdb File Name. + + The portion of the Pdb File Name between the last backslash and + either a following period or the end of the string is converted + to Unicode and copied into UnicodeBuffer. The name is truncated, + if necessary, to ensure that UnicodeBuffer is not overrun. + + @param[in] PdbFileName Pdb file name. + @param[out] UnicodeBuffer The resultant Unicode File Name. + +**/ +VOID +GetShortPdbFileName ( + IN CHAR8 *PdbFileName, + OUT CHAR16 *UnicodeBuffer + ) +{ + UINTN IndexA; // Current work location within an ASCII string. + UINTN IndexU; // Current work location within a Unicode string. + UINTN StartIndex; + UINTN EndIndex; + + ZeroMem (UnicodeBuffer, DXE_PERFORMANCE_STRING_LENGTH * sizeof (CHAR16)); + + if (PdbFileName == NULL) { + StrCpy (UnicodeBuffer, L" "); + } else { + StartIndex = 0; + for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++) + ; + for (IndexA = 0; PdbFileName[IndexA] != 0; IndexA++) { + if (PdbFileName[IndexA] == '\\') { + StartIndex = IndexA + 1; + } + + if (PdbFileName[IndexA] == '.') { + EndIndex = IndexA; + } + } + + IndexU = 0; + for (IndexA = StartIndex; IndexA < EndIndex; IndexA++) { + UnicodeBuffer[IndexU] = (CHAR16) PdbFileName[IndexA]; + IndexU++; + if (IndexU >= DXE_PERFORMANCE_STRING_LENGTH) { + UnicodeBuffer[DXE_PERFORMANCE_STRING_LENGTH] = 0; + break; + } + } + } +} + +/** + Get a human readable name for an image handle. + The following methods will be tried orderly: + 1. Image PDB + 2. ComponentName2 protocol + 3. FFS UI section + 4. Image GUID + 5. Image DevicePath + 6. Unknown Driver Name + + @param[in] Handle + + @post The resulting Unicode name string is stored in the + mGaugeString global array. + +**/ +VOID +GetNameFromHandle ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *Image; + CHAR8 *PdbFileName; + EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; + EFI_STRING StringPtr; + EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_GUID *NameGuid; + CHAR16 *NameString; + UINTN StringSize; + CHAR8 *PlatformLanguage; + EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevicePathToText; + + // + // Method 1: Get the name string from image PDB + // + Status = gBS->HandleProtocol ( + Handle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &Image + ); + + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Handle, + &gEfiDriverBindingProtocolGuid, + (VOID **) &DriverBinding, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + Status = gBS->HandleProtocol ( + DriverBinding->ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &Image + ); + } + } + + if (!EFI_ERROR (Status)) { + PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase); + + if (PdbFileName != NULL) { + GetShortPdbFileName (PdbFileName, mGaugeString); + return; + } + } + + // + // Method 2: Get the name string from ComponentName2 protocol + // + Status = gBS->HandleProtocol ( + Handle, + &gEfiComponentName2ProtocolGuid, + (VOID **) &ComponentName2 + ); + if (!EFI_ERROR (Status)) { + // + // Get the current platform language setting + // + GetEfiGlobalVariable2 (L"PlatformLang", (VOID**)&PlatformLanguage, NULL); + Status = ComponentName2->GetDriverName ( + ComponentName2, + PlatformLanguage != NULL ? PlatformLanguage : "en-US", + &StringPtr + ); + if (!EFI_ERROR (Status)) { + SHELL_FREE_NON_NULL (PlatformLanguage); + StrnCpy (mGaugeString, StringPtr, DP_GAUGE_STRING_LENGTH); + mGaugeString[DP_GAUGE_STRING_LENGTH] = 0; + return; + } + } + + Status = gBS->HandleProtocol ( + Handle, + &gEfiLoadedImageDevicePathProtocolGuid, + (VOID **) &LoadedImageDevicePath + ); + if (!EFI_ERROR (Status) && (LoadedImageDevicePath != NULL)) { + DevicePath = LoadedImageDevicePath; + + // + // Try to get image GUID from LoadedImageDevicePath protocol + // + NameGuid = NULL; + while (!IsDevicePathEndType (DevicePath)) { + NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath); + if (NameGuid != NULL) { + break; + } + DevicePath = NextDevicePathNode (DevicePath); + } + + if (NameGuid != NULL) { + // + // Try to get the image's FFS UI section by image GUID + // + NameString = NULL; + StringSize = 0; + Status = GetSectionFromAnyFv ( + NameGuid, + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **) &NameString, + &StringSize + ); + + if (!EFI_ERROR (Status)) { + // + // Method 3. Get the name string from FFS UI section + // + StrnCpy (mGaugeString, NameString, DP_GAUGE_STRING_LENGTH); + mGaugeString[DP_GAUGE_STRING_LENGTH] = 0; + FreePool (NameString); + } else { + // + // Method 4: Get the name string from image GUID + // + UnicodeSPrint (mGaugeString, sizeof (mGaugeString), L"%g", NameGuid); + } + return; + } else { + // + // Method 5: Get the name string from image DevicePath + // + Status = gBS->LocateProtocol ( + &gEfiDevicePathToTextProtocolGuid, + NULL, + (VOID **) &DevicePathToText + ); + if (!EFI_ERROR (Status)) { + NameString = DevicePathToText->ConvertDevicePathToText (LoadedImageDevicePath, TRUE, FALSE); + if (NameString != NULL) { + StrnCpy (mGaugeString, NameString, DP_GAUGE_STRING_LENGTH); + mGaugeString[DP_GAUGE_STRING_LENGTH] = 0; + FreePool (NameString); + return; + } + } + } + } + + // + // Method 6: Unknown Driver Name + // + StringPtr = HiiGetString (gDpHiiHandle, STRING_TOKEN (STR_DP_ERROR_NAME), NULL); + ASSERT (StringPtr != NULL); + StrCpy (mGaugeString, StringPtr); + FreePool (StringPtr); +} + +/** + Calculate the Duration in microseconds. + + Duration is multiplied by 1000, instead of Frequency being divided by 1000 or + multiplying the result by 1000, in order to maintain precision. Since Duration is + a 64-bit value, multiplying it by 1000 is unlikely to produce an overflow. + + The time is calculated as (Duration * 1000) / Timer_Frequency. + + @param[in] Duration The event duration in timer ticks. + + @return A 64-bit value which is the Elapsed time in microseconds. +**/ +UINT64 +DurationInMicroSeconds ( + IN UINT64 Duration + ) +{ + UINT64 Temp; + + Temp = MultU64x32 (Duration, 1000); + return DivU64x32 (Temp, TimerInfo.Frequency); +} + +/** + Get index of Measurement Record's match in the CumData array. + + If the Measurement's Token value matches a Token in one of the CumData + records, the index of the matching record is returned. The returned + index is a signed value so that negative values can indicate that + the Measurement didn't match any entry in the CumData array. + + @param[in] Measurement A pointer to a Measurement Record to match against the CumData array. + + @retval <0 Token is not in the CumData array. + @retval >=0 Return value is the index into CumData where Token is found. +**/ +INTN +GetCumulativeItem( + IN MEASUREMENT_RECORD *Measurement + ) +{ + INTN Index; + + for( Index = 0; Index < (INTN)NumCum; ++Index) { + if (AsciiStrnCmp (Measurement->Token, CumData[Index].Name, PERF_TOKEN_LENGTH) == 0) { + return Index; // Exit, we found a match + } + } + // If the for loop exits, Token was not found. + return -1; // Indicate failure +} diff --git a/ShellPkg/Library/UefiDpLib/Literals.c b/ShellPkg/Library/UefiDpLib/Literals.c new file mode 100644 index 0000000000..68de0fbc9e --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/Literals.c @@ -0,0 +1,23 @@ +/** @file + Definitions of ASCII string literals used by DP. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ +#include + +// ASCII String literals which probably don't need translation +CHAR8 const ALit_TimerLibError[] = "Timer library instance error!\n"; +CHAR8 const ALit_SEC[] = SEC_TOK; +CHAR8 const ALit_DXE[] = DXE_TOK; +CHAR8 const ALit_SHELL[] = SHELL_TOK; +CHAR8 const ALit_PEI[] = PEI_TOK; +CHAR8 const ALit_BDS[] = BDS_TOK; +CHAR8 const ALit_BdsTO[] = "BdsTimeOut"; +CHAR8 const ALit_PEIM[] = "PEIM"; diff --git a/ShellPkg/Library/UefiDpLib/Literals.h b/ShellPkg/Library/UefiDpLib/Literals.h new file mode 100644 index 0000000000..8aec09c024 --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/Literals.h @@ -0,0 +1,26 @@ +/** @file + Declarations of ASCII string literals used by DP. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ +#ifndef _LITERALS_H_ +#define _LITERALS_H_ + +// ASCII String literals which probably don't need translation +extern CHAR8 const ALit_TimerLibError[]; +extern CHAR8 const ALit_SEC[]; +extern CHAR8 const ALit_DXE[]; +extern CHAR8 const ALit_SHELL[]; +extern CHAR8 const ALit_PEI[]; +extern CHAR8 const ALit_BDS[]; +extern CHAR8 const ALit_BdsTO[]; +extern CHAR8 const ALit_PEIM[]; + +#endif // _LITERALS_H_ diff --git a/ShellPkg/Library/UefiDpLib/UefiDpLib.c b/ShellPkg/Library/UefiDpLib/UefiDpLib.c new file mode 100644 index 0000000000..f3ecfeac7f --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/UefiDpLib.c @@ -0,0 +1,101 @@ +/** @file + Main file for NULL named library for install1 shell command functions. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UefiDpLib.h" + +STATIC CONST CHAR16 mFileName[] = L"ShellCommands"; +EFI_HANDLE gDpHiiHandle = NULL; + +#define DP_HII_GUID \ + { \ + 0xeb832fd9, 0x9089, 0x4898, { 0x83, 0xc9, 0x41, 0x61, 0x8f, 0x5c, 0x48, 0xb9 } \ + } + +EFI_GUID gDpHiiGuid = DP_HII_GUID; + +/** + Function to get the filename with help context if HII will not be used. + + @return The filename with help text in it. +**/ +CONST CHAR16* +EFIAPI +UefiDpLibGetManFileName ( + VOID + ) +{ + return (mFileName); +} + +/** + Constructor for the Shell Level 1 Commands library. + + Install the handlers for level 1 UEFI Shell 2.0 commands. + + @param ImageHandle the image handle of the process + @param SystemTable the EFI System Table pointer + + @retval EFI_SUCCESS the shell command handlers were installed sucessfully + @retval EFI_UNSUPPORTED the shell level required was not found. +**/ +EFI_STATUS +EFIAPI +UefiDpLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + // + // check our bit of the profiles mask + // + if ((PcdGet8(PcdShellProfileMask) & BIT2) == 0) { + return (EFI_SUCCESS); + } + + // + // 3rd parameter 'HII strings array' must be name of .uni strings file followed by 'Strings', e.g. mycommands.uni must be + // specified as 'mycommandsStrings' because the build Autogen process defines this as a string array for the strings in your + // .uni file. Examine your Build folder under your package's DEBUG folder and you will find it defined in a xxxStrDefs.h file. + // + gDpHiiHandle = HiiAddPackages (&gDpHiiGuid, gImageHandle, UefiDpLibStrings, NULL); + if (gDpHiiHandle == NULL) { + return (EFI_DEVICE_ERROR); + } + + // + // install our shell command handlers that are always installed + // + ShellCommandRegisterCommandName(L"dp", ShellCommandRunDp , UefiDpLibGetManFileName, 0, L"", FALSE, gDpHiiHandle, STRING_TOKEN(STR_GET_HELP_DP)); + + return (EFI_SUCCESS); +} + +/** + Destructor for the library. free any resources. + + @param ImageHandle The image handle of the process. + @param SystemTable The EFI System Table pointer. +**/ +EFI_STATUS +EFIAPI +UefiDpLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + if (gDpHiiHandle != NULL) { + HiiRemovePackages(gDpHiiHandle); + } + return (EFI_SUCCESS); +} diff --git a/ShellPkg/Library/UefiDpLib/UefiDpLib.h b/ShellPkg/Library/UefiDpLib/UefiDpLib.h new file mode 100644 index 0000000000..2cec991483 --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/UefiDpLib.h @@ -0,0 +1,60 @@ +/** @file + Main file for NULL named library for dp command functions. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UEFI_DP_LIB_H_ +#define _UEFI_DP_LIB_H_ + +#include +#include + +extern EFI_GUID gDpHiiGuid; + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern EFI_HANDLE gDpHiiHandle; + +/** + Function for 'dp' command. + + @param[in] ImageHandle Handle to the Image (NULL if Internal). + @param[in] SystemTable Pointer to the System Table (NULL if Internal). +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunDp ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +#endif + diff --git a/ShellPkg/Library/UefiDpLib/UefiDpLib.inf b/ShellPkg/Library/UefiDpLib/UefiDpLib.inf new file mode 100644 index 0000000000..43de57dcfc --- /dev/null +++ b/ShellPkg/Library/UefiDpLib/UefiDpLib.inf @@ -0,0 +1,75 @@ +## @file +# Display Performance Application, Module information file. +# +# Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved. +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = UefiDpLib + FILE_GUID = 9DF262F7-CF81-4294-B5A5-B2E3CAFE5618 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = UefiDpLibConstructor + DESTRUCTOR = UefiDpLibDestructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + UefiDpLib.c + UefiDpLib.h + UefiDpLib.uni + Dp.c + Dp.h + Literals.h + Literals.c + DpInternal.h + DpUtilities.c + DpTrace.c + DpProfile.c + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + MdeModulePkg/MdeModulePkg.dec + PerformancePkg/PerformancePkg.dec + +[LibraryClasses] + TimerLib + PerformanceLib + DxeServicesLib + MemoryAllocationLib + BaseLib + BaseMemoryLib + DebugLib + ShellCommandLib + ShellLib + UefiLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + SortLib + PrintLib + +[Protocols] + gEfiLoadedImageProtocolGuid # ALWAYS_CONSUMED + gEfiDriverBindingProtocolGuid # SOMETIMES_CONSUMED + gEfiComponentName2ProtocolGuid # SOMETIMES_CONSUMED + gEfiLoadedImageDevicePathProtocolGuid # SOMETIMES_CONSUMED + gEfiDevicePathToTextProtocolGuid # SOMETIMES_CONSUMED + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize + gEfiShellPkgTokenSpaceGuid.PcdShellProfileMask # ALWAYS_CONSUMED diff --git a/ShellPkg/Library/UefiDpLib/UefiDpLib.uni b/ShellPkg/Library/UefiDpLib/UefiDpLib.uni new file mode 100644 index 0000000000000000000000000000000000000000..fa29f9be4aa5268f4f1b30f8ab3df9163994544e GIT binary patch literal 17140 zcmd^{Yj0e)5r+A>K>x!+fdD79WIINR)@T~YTG2 z4Ci4SUWcE;h05Lv2jNSdzYceG{83j+)4O}&ez>jc_3%a|kHU$rp>Qr5{C%aTV_m%o z%X+h-_moJqC-LqmO!Vz}c&ERID*31IFl>iES5WN=$ylWZqI)iSuOsbK-MF z;wau92<=%oRY~4`)OT-GexN6yp`Nj>PDIUH50&%y^@-jc=;}>G1YM`k*N4*@-syTQ z3a`Sx{!;5@L;>xD#OgYYsGSPPz&oDtg*KgqjB)d$O6^a-Kb!IZOcxc-qCppu z#e=A4=$3Jp^5>D)%WD6*By>jzs@$LG$-b~3O55@`%cAm=eu*dPYBxLyYr-{>25p7M z`nwXI>V92Z9f+@-clC5#I`&LgYmwK_!luq^dJa#FLXyL}bb3uCb|U31U7^eTUCntZ@)tsMK~uwyEdqq6&?zSo^l_z|J%3+*Mi1P^*=Cq_Wih zSDnAt8<5in*QT{tE7rnaW^@wub@5^(&7#hsdYbdCejcdYnK+MDpdXu(>Zz_CG`<+< zEqr||bf#tWwuxxdC&n%1SE5uYi}qf0(yY=O#J+bddeC$oo~Y*Is6DCI?L>_>T|N}5 zf$G2-(0}Q9&6aMfUGGG7q946;AW2!DK`%eH;#-nVel@k5M)BM5MWoH$7vdVVy`G{r z9-;S3(g38fApY|ny`Bj1LgicUt!A}MTld7_QP|VhBYjg*2hG_L91tW?B98HTotTy!t4qA31)$#}Cqx63$>LR%cdXFY;(b z8e^8A$*B^~Q1`bwUW(I;(u+a(G2Zjck5YWtsOPd-*qx#D3G4TGjs#2a5pm#$=|SeF z@gbMdN)e0P66P;MDZ(ewq7H=+?b?f+!++op+Kv*08?q(`LWoCAvCore84jaeg{#C2 zL^MPWL{*ErDn*Ta4dtFVjGx2vbsr_DpNX$O>vtC3>o1m_$brZVgzg7Qv=aZiE{-B` z{MmEe?dWIzvx%|<9h|`u;%P61>m4$z8)6y^?UW8ztyNT@unvw z$amyPu&zhaP@)s~X}-%m6HzCz$2e**yndt-N8ul8Cl>o4dLpZHQU3l9vE{cE0Xzx+ z?4jwz66gtb-D0Fsyi0LNY8O|(Oxy6u#0MAAvY352R(tW(1C230NM0NIi(fy>o|N{D z=do1}rEOTpJ#qF))IvPSR@BP=?R+Xpyb@v}SLBFXtOZ0#lh_s_Z+t&J`L^o8Hl0eU z2eEaGb9@Xy@Aq*B{f(I0qV>1Z>sOMujfP5FT=xPawG@xp^Gw)AARJQ?XIM~ zi{Tqdrc7SVww5ACE{v5n)Ym^1^4-`gi81oF*SXs*sZ#XyzB?4>*X1#>({0gi3E!)p z8(Xt4i^AB(#z9!D5-(njjP|B|@1`0-?1tWcnCf^HQ_kt;UA0vy9%+`VJ{d-vg#{jK zMChYOFX2A-mU>ek+mLgH__keEUQdb4=qra)uDp~7xi235IMqJxelLVhZ!z4{QN1@S zSE_S4{#*mUp4H;Dl|=%%WXt8+SE}6-txMDKGxE3|XI!#vJTdD1B%8~%D%Gk)lO;5r zD8-)35${W0=xM7J0euN}r%~qjy>yASC`FL#6~5#w`asLE4rD2@(nQKG@3u2kh{3Ud z7H>a}V-&Kj-3MOaXHh1mv!kwKrLsw!ah-CC?dKBNR4sSPSRuUd(ae1G;!()Jr&%q*|A$N!06vqDzu1 zyho`-b2;uQBSXHKh=VA~Ig!pKxF5%U!q}GnSQ1}6R$W^$Z%*WvV@h?Ws}dARuhMr{ zUwLlh>XP*m-QI|jbsZ~}PtKXEEk+4vT(1_Tqu*3w2XyEDjiQMhZ7#>Q{!oJUe$|CJmMVST!M5}Uinl%G8z~1 z*<=z+>_S`J$v*5!SF1?Mu-uyUMWW_$^yu~6%yJxGJMA{~_HJdG6ZKgY|AP4^@+Cwj z#2WsbY%nuA_O#RnT*ZaD7L`cE5OrI-RUx(9oMUh1SNu(VB;5D;P5EH6qdPM$d;KMR zuxOji1jphuLuCH+o^p_6RZ8zl+mYKv^x5ZkxniCf*irhO{(5~z$5xGU!%wyYXh_uS#L63;W^}$=j z%+spk3!C*`6OXGFtfWOL*Y3Gm?1s%Llczn3J$?BG7*ozIm%Z`+dKj5g@VqT~7P8K1 z?FXx9$WmJ__a@k@IfN$qe%L{TcNmDj*vh4-l^Zb@K$obH_&k^J?W?33t+1;J&oq@&$Jl&5#31*+rrq-djY&Y*?v1a?C@;2EP8L9(PvT zQG)sNU2{`M)|X4HPh#!O_9w^!eg1mya_=)9c75_bFi)3$={`VjxJk~8c=?5_L08;d z(ie;dHpNy%mW_|={=leod$2x8a&J}Qove0dUXzV*-Do?PpsDx7U&no^y>X6vFKXj* z^;rqW-ILHO?rE-ZLu-h;uX#hx&_>XH-4}ndmsPaQ>k|Ck_A;OMu2Z)WG&vjFy-?47 zP<}0rKf2%7Y1(}=a%eBt{%48{M6?rps%dAas=!HhLcT9)1YL6S>03EGUzSPx~Z!0Yd?SC8$g zVcsUKR7GpZv{Y9eg6=dv{TTCH?bvp-V+nbVgq4-xb&STBm&*I?$2Ct}y_;RxIw+no zhW(d*OKPiMZO_W>&aOtYEAZQ10al2xo(ejw=OAlj-rk>%XYbixXZ3uiXK=fY*tvdH z%V<$kb9j=k_a*lYDw{7Odt@1A^up$uCJhYmsEjuI7pV`r)MJ?``hQx7?(ga?U5ab5 z+v*`7$Nni>3*FWt_BK)rb+gavP#m|qyJrM^R~K39`s^XpY#n_YH_(o@qt0noaoesi z_IMe>CLc^9Co0k82-??n)N7)xCo0+9TDU_G8>pPk;xud9Zmp}y@6y^BYrYrFYMYz2 z?iSXX#;)_)iPp9Aa#UDPJ~F*tU04I+vkm!}YF3(TDDUd>V?{qY&OW^};b$%j8<=WR(kXlZpuDEuDzTXAwga5q> zM|}t3Nd1>F3OkvY(>3e3B}&Y$^~3xyPABx<&s!%^+eT5K~_-Mq&>pbaA(>_qgv>bXuh?{iJ)l6FD`*YSaG zb@+OftE6E2*Y||xk#xfLsPk-ls;`F35W%@r8yuy!<7hnnE**!6W3%Mu6E3chpy We<7R8eXdz +# Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.
# # This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License @@ -64,7 +64,7 @@ gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|16000 !ifdef $(NO_SHELL_PROFILES) gEfiShellPkgTokenSpaceGuid.PcdShellProfileMask|0x00 -!endif +!endif #$(NO_SHELL_PROFILES) [Components] ShellPkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf @@ -73,6 +73,13 @@ ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf + ShellPkg/Library/UefiDpLib/UefiDpLib.inf { + + TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf + PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf + DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf + } + ShellPkg/Application/Shell/Shell.inf { NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf @@ -83,6 +90,9 @@ NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf -!endif +!ifdef $(INCLUDE_DP) + NULL|ShellPkg/Library/UefiDpLib/UefiDpLib.inf +!endif #$(INCLUDE_DP) +!endif #$(NO_SHELL_PROFILES) } -- 2.39.2