Add new interface GetVariable2 and GetEfiGlobalVariable2 to return more info. Also...
[mirror_edk2.git] / PerformancePkg / Dp_App / DpUtilities.c
1 /** @file
2 Utility functions used by the Dp application.
3
4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 **/
13
14 #include <Library/BaseLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/TimerLib.h>
20 #include <Library/PeCoffGetEntryPointLib.h>
21 #include <Library/PrintLib.h>
22 #include <Library/HiiLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/UefiLib.h>
25 #include <Library/DevicePathLib.h>
26
27 #include <Pi/PiFirmwareFile.h>
28 #include <Library/DxeServicesLib.h>
29
30 #include <Protocol/LoadedImage.h>
31 #include <Protocol/DriverBinding.h>
32 #include <Protocol/ComponentName2.h>
33 #include <Protocol/DevicePath.h>
34 #include <Protocol/DevicePathToText.h>
35
36 #include <Guid/Performance.h>
37
38 #include "Dp.h"
39 #include "Literals.h"
40 #include "DpInternal.h"
41
42 /**
43 Wrap original FreePool to check NULL pointer first.
44
45 @param[in] Buffer The pointer to the buffer to free.
46
47 **/
48 VOID
49 SafeFreePool (
50 IN VOID *Buffer
51 )
52 {
53 if (Buffer != NULL) {
54 FreePool (Buffer);
55 }
56 }
57
58 /**
59 Calculate an event's duration in timer ticks.
60
61 Given the count direction and the event's start and end timer values,
62 calculate the duration of the event in timer ticks. Information for
63 the current measurement is pointed to by the parameter.
64
65 If the measurement's start time is 1, it indicates that the developer
66 is indicating that the measurement began at the release of reset.
67 The start time is adjusted to the timer's starting count before performing
68 the elapsed time calculation.
69
70 The calculated duration, in ticks, is the absolute difference between
71 the measurement's ending and starting counts.
72
73 @param Measurement Pointer to a MEASUREMENT_RECORD structure containing
74 data for the current measurement.
75
76 @return The 64-bit duration of the event.
77 **/
78 UINT64
79 GetDuration (
80 IN OUT MEASUREMENT_RECORD *Measurement
81 )
82 {
83 UINT64 Duration;
84 BOOLEAN Error;
85
86 // PERF_START macros are called with a value of 1 to indicate
87 // the beginning of time. So, adjust the start ticker value
88 // to the real beginning of time.
89 // Assumes no wraparound. Even then, there is a very low probability
90 // of having a valid StartTicker value of 1.
91 if (Measurement->StartTimeStamp == 1) {
92 Measurement->StartTimeStamp = TimerInfo.StartCount;
93 }
94 if (TimerInfo.CountUp) {
95 Duration = Measurement->EndTimeStamp - Measurement->StartTimeStamp;
96 Error = (BOOLEAN)(Duration > Measurement->EndTimeStamp);
97 }
98 else {
99 Duration = Measurement->StartTimeStamp - Measurement->EndTimeStamp;
100 Error = (BOOLEAN)(Duration > Measurement->StartTimeStamp);
101 }
102
103 if (Error) {
104 DEBUG ((EFI_D_ERROR, ALit_TimerLibError));
105 Duration = 0;
106 }
107 return Duration;
108 }
109
110 /**
111 Determine whether the Measurement record is for an EFI Phase.
112
113 The Token and Module members of the measurement record are checked.
114 Module must be empty and Token must be one of SEC, PEI, DXE, BDS, or SHELL.
115
116 @param[in] Measurement A pointer to the Measurement record to test.
117
118 @retval TRUE The measurement record is for an EFI Phase.
119 @retval FALSE The measurement record is NOT for an EFI Phase.
120 **/
121 BOOLEAN
122 IsPhase(
123 IN MEASUREMENT_RECORD *Measurement
124 )
125 {
126 BOOLEAN RetVal;
127
128 RetVal = (BOOLEAN)( ( *Measurement->Module == '\0') &&
129 ((AsciiStrnCmp (Measurement->Token, ALit_SEC, PERF_TOKEN_LENGTH) == 0) ||
130 (AsciiStrnCmp (Measurement->Token, ALit_PEI, PERF_TOKEN_LENGTH) == 0) ||
131 (AsciiStrnCmp (Measurement->Token, ALit_DXE, PERF_TOKEN_LENGTH) == 0) ||
132 (AsciiStrnCmp (Measurement->Token, ALit_BDS, PERF_TOKEN_LENGTH) == 0))
133 );
134 return RetVal;
135 }
136
137 /**
138 Get the file name portion of the Pdb File Name.
139
140 The portion of the Pdb File Name between the last backslash and
141 either a following period or the end of the string is converted
142 to Unicode and copied into UnicodeBuffer. The name is truncated,
143 if necessary, to ensure that UnicodeBuffer is not overrun.
144
145 @param[in] PdbFileName Pdb file name.
146 @param[out] UnicodeBuffer The resultant Unicode File Name.
147
148 **/
149 VOID
150 GetShortPdbFileName (
151 IN CHAR8 *PdbFileName,
152 OUT CHAR16 *UnicodeBuffer
153 )
154 {
155 UINTN IndexA; // Current work location within an ASCII string.
156 UINTN IndexU; // Current work location within a Unicode string.
157 UINTN StartIndex;
158 UINTN EndIndex;
159
160 ZeroMem (UnicodeBuffer, DXE_PERFORMANCE_STRING_LENGTH * sizeof (CHAR16));
161
162 if (PdbFileName == NULL) {
163 StrCpy (UnicodeBuffer, L" ");
164 } else {
165 StartIndex = 0;
166 for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++)
167 ;
168 for (IndexA = 0; PdbFileName[IndexA] != 0; IndexA++) {
169 if (PdbFileName[IndexA] == '\\') {
170 StartIndex = IndexA + 1;
171 }
172
173 if (PdbFileName[IndexA] == '.') {
174 EndIndex = IndexA;
175 }
176 }
177
178 IndexU = 0;
179 for (IndexA = StartIndex; IndexA < EndIndex; IndexA++) {
180 UnicodeBuffer[IndexU] = (CHAR16) PdbFileName[IndexA];
181 IndexU++;
182 if (IndexU >= DXE_PERFORMANCE_STRING_LENGTH) {
183 UnicodeBuffer[DXE_PERFORMANCE_STRING_LENGTH] = 0;
184 break;
185 }
186 }
187 }
188 }
189
190 /**
191 Get a human readable name for an image handle.
192 The following methods will be tried orderly:
193 1. Image PDB
194 2. ComponentName2 protocol
195 3. FFS UI section
196 4. Image GUID
197 5. Image DevicePath
198 6. Unknown Driver Name
199
200 @param[in] Handle
201
202 @post The resulting Unicode name string is stored in the
203 mGaugeString global array.
204
205 **/
206 VOID
207 GetNameFromHandle (
208 IN EFI_HANDLE Handle
209 )
210 {
211 EFI_STATUS Status;
212 EFI_LOADED_IMAGE_PROTOCOL *Image;
213 CHAR8 *PdbFileName;
214 EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
215 EFI_STRING StringPtr;
216 EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath;
217 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
218 EFI_GUID *NameGuid;
219 CHAR16 *NameString;
220 UINTN StringSize;
221 CHAR8 *PlatformLanguage;
222 EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
223 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevicePathToText;
224
225 //
226 // Method 1: Get the name string from image PDB
227 //
228 Status = gBS->HandleProtocol (
229 Handle,
230 &gEfiLoadedImageProtocolGuid,
231 (VOID **) &Image
232 );
233
234 if (EFI_ERROR (Status)) {
235 Status = gBS->OpenProtocol (
236 Handle,
237 &gEfiDriverBindingProtocolGuid,
238 (VOID **) &DriverBinding,
239 NULL,
240 NULL,
241 EFI_OPEN_PROTOCOL_GET_PROTOCOL
242 );
243 if (!EFI_ERROR (Status)) {
244 Status = gBS->HandleProtocol (
245 DriverBinding->ImageHandle,
246 &gEfiLoadedImageProtocolGuid,
247 (VOID **) &Image
248 );
249 }
250 }
251
252 if (!EFI_ERROR (Status)) {
253 PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase);
254
255 if (PdbFileName != NULL) {
256 GetShortPdbFileName (PdbFileName, mGaugeString);
257 return;
258 }
259 }
260
261 //
262 // Method 2: Get the name string from ComponentName2 protocol
263 //
264 Status = gBS->HandleProtocol (
265 Handle,
266 &gEfiComponentName2ProtocolGuid,
267 (VOID **) &ComponentName2
268 );
269 if (!EFI_ERROR (Status)) {
270 //
271 // Get the current platform language setting
272 //
273 GetEfiGlobalVariable2 (L"PlatformLang", &PlatformLanguage, NULL);
274 Status = ComponentName2->GetDriverName (
275 ComponentName2,
276 PlatformLanguage != NULL ? PlatformLanguage : "en-US",
277 &StringPtr
278 );
279 if (!EFI_ERROR (Status)) {
280 SafeFreePool (PlatformLanguage);
281 StrnCpy (mGaugeString, StringPtr, DP_GAUGE_STRING_LENGTH);
282 mGaugeString[DP_GAUGE_STRING_LENGTH] = 0;
283 return;
284 }
285 }
286
287 Status = gBS->HandleProtocol (
288 Handle,
289 &gEfiLoadedImageDevicePathProtocolGuid,
290 (VOID **) &LoadedImageDevicePath
291 );
292 if (!EFI_ERROR (Status)) {
293 DevicePath = LoadedImageDevicePath;
294
295 //
296 // Try to get image GUID from LoadedImageDevicePath protocol
297 //
298 NameGuid = NULL;
299 while (!IsDevicePathEndType (DevicePath)) {
300 NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);
301 if (NameGuid != NULL) {
302 break;
303 }
304 DevicePath = NextDevicePathNode (DevicePath);
305 }
306
307 if (NameGuid != NULL) {
308 //
309 // Try to get the image's FFS UI section by image GUID
310 //
311 NameString = NULL;
312 StringSize = 0;
313 Status = GetSectionFromAnyFv (
314 NameGuid,
315 EFI_SECTION_USER_INTERFACE,
316 0,
317 (VOID **) &NameString,
318 &StringSize
319 );
320
321 if (!EFI_ERROR (Status)) {
322 //
323 // Method 3. Get the name string from FFS UI section
324 //
325 StrnCpy (mGaugeString, NameString, DP_GAUGE_STRING_LENGTH);
326 mGaugeString[DP_GAUGE_STRING_LENGTH] = 0;
327 FreePool (NameString);
328 } else {
329 //
330 // Method 4: Get the name string from image GUID
331 //
332 UnicodeSPrint (mGaugeString, sizeof (mGaugeString), L"%g", NameGuid);
333 }
334 return;
335 } else {
336 //
337 // Method 5: Get the name string from image DevicePath
338 //
339 Status = gBS->LocateProtocol (
340 &gEfiDevicePathToTextProtocolGuid,
341 NULL,
342 (VOID **) &DevicePathToText
343 );
344 if (!EFI_ERROR (Status)) {
345 NameString = DevicePathToText->ConvertDevicePathToText (LoadedImageDevicePath, TRUE, FALSE);
346 if (NameString != NULL) {
347 StrnCpy (mGaugeString, NameString, DP_GAUGE_STRING_LENGTH);
348 mGaugeString[DP_GAUGE_STRING_LENGTH] = 0;
349 FreePool (NameString);
350 return;
351 }
352 }
353 }
354 }
355
356 //
357 // Method 6: Unknown Driver Name
358 //
359 StringPtr = HiiGetString (gHiiHandle, STRING_TOKEN (STR_DP_ERROR_NAME), NULL);
360 ASSERT (StringPtr != NULL);
361 StrCpy (mGaugeString, StringPtr);
362 FreePool (StringPtr);
363 return;
364 }
365
366 /**
367 Calculate the Duration in microseconds.
368
369 Duration is multiplied by 1000, instead of Frequency being divided by 1000 or
370 multiplying the result by 1000, in order to maintain precision. Since Duration is
371 a 64-bit value, multiplying it by 1000 is unlikely to produce an overflow.
372
373 The time is calculated as (Duration * 1000) / Timer_Frequency.
374
375 @param[in] Duration The event duration in timer ticks.
376
377 @return A 64-bit value which is the Elapsed time in microseconds.
378 **/
379 UINT64
380 DurationInMicroSeconds (
381 IN UINT64 Duration
382 )
383 {
384 UINT64 Temp;
385
386 Temp = MultU64x32 (Duration, 1000);
387 return DivU64x32 (Temp, TimerInfo.Frequency);
388 }
389
390 /**
391 Formatted Print using a Hii Token to reference the localized format string.
392
393 @param[in] Token A HII token associated with a localized Unicode string.
394 @param[in] ... The variable argument list.
395
396 @return The number of characters converted by UnicodeVSPrint().
397
398 **/
399 UINTN
400 PrintToken (
401 IN UINT16 Token,
402 ...
403 )
404 {
405 VA_LIST Marker;
406 EFI_STRING StringPtr;
407 UINTN Return;
408 UINTN BufferSize;
409
410 StringPtr = HiiGetString (gHiiHandle, Token, NULL);
411 ASSERT (StringPtr != NULL);
412
413 VA_START (Marker, Token);
414
415 BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);
416
417 if (mPrintTokenBuffer == NULL) {
418 mPrintTokenBuffer = AllocatePool (BufferSize);
419 ASSERT (mPrintTokenBuffer != NULL);
420 }
421 SetMem( mPrintTokenBuffer, BufferSize, 0);
422
423 Return = UnicodeVSPrint (mPrintTokenBuffer, BufferSize, StringPtr, Marker);
424 VA_END (Marker);
425
426 if (Return > 0 && gST->ConOut != NULL) {
427 gST->ConOut->OutputString (gST->ConOut, mPrintTokenBuffer);
428 }
429 FreePool (StringPtr);
430 return Return;
431 }
432
433 /**
434 Get index of Measurement Record's match in the CumData array.
435
436 If the Measurement's Token value matches a Token in one of the CumData
437 records, the index of the matching record is returned. The returned
438 index is a signed value so that negative values can indicate that
439 the Measurement didn't match any entry in the CumData array.
440
441 @param[in] Measurement A pointer to a Measurement Record to match against the CumData array.
442
443 @retval <0 Token is not in the CumData array.
444 @retval >=0 Return value is the index into CumData where Token is found.
445 **/
446 INTN
447 GetCumulativeItem(
448 IN MEASUREMENT_RECORD *Measurement
449 )
450 {
451 INTN Index;
452
453 for( Index = 0; Index < (INTN)NumCum; ++Index) {
454 if (AsciiStrnCmp (Measurement->Token, CumData[Index].Name, PERF_TOKEN_LENGTH) == 0) {
455 return Index; // Exit, we found a match
456 }
457 }
458 // If the for loop exits, Token was not found.
459 return -1; // Indicate failure
460 }