Libraries and utilities for instrumenting regions of code and measuring their perform...
[mirror_edk2.git] / PerformancePkg / Dp_App / DpUtilities.c
1 /** @file
2 * Utility functions used by the Dp application.
3 *
4 * Copyright (c) 2009-2010, 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
25 #include <Protocol/LoadedImage.h>
26 #include <Protocol/Driverbinding.h>
27
28 #include <Guid/Performance.h>
29
30 #include "Dp.h"
31 #include "Literals.h"
32 #include "DpInternal.h"
33
34 /** Calculate an event's duration in timer ticks.
35 *
36 * Given the count direction and the event's start and end timer values,
37 * calculate the duration of the event in timer ticks. Information for
38 * the current measurement is pointed to by the parameter.
39 *
40 * If the measurement's start time is 1, it indicates that the developer
41 * is indicating that the measurement began at the release of reset.
42 * The start time is adjusted to the timer's starting count before performing
43 * the elapsed time calculation.
44 *
45 * The calculated duration, in ticks, is the absolute difference between
46 * the measurement's ending and starting counts.
47 *
48 * @pre The global TimerInfo structure must have already been initialized
49 * before this function is called.
50 *
51 * @param[in,out] Measurement Pointer to a MEASUREMENT_RECORD structure containing
52 * data for the current measurement.
53 *
54 * @return The 64-bit duration of the event.
55 **/
56 UINT64
57 GetDuration (
58 IN OUT MEASUREMENT_RECORD *Measurement
59 )
60 {
61 UINT64 Duration;
62 BOOLEAN Error;
63
64 // PERF_START macros are called with a value of 1 to indicate
65 // the beginning of time. So, adjust the start ticker value
66 // to the real beginning of time.
67 // Assumes no wraparound. Even then, there is a very low probability
68 // of having a valid StartTicker value of 1.
69 if (Measurement->StartTimeStamp == 1) {
70 Measurement->StartTimeStamp = TimerInfo.StartCount;
71 }
72 if (TimerInfo.CountUp) {
73 Duration = Measurement->EndTimeStamp - Measurement->StartTimeStamp;
74 Error = (BOOLEAN)(Duration > Measurement->EndTimeStamp);
75 }
76 else {
77 Duration = Measurement->StartTimeStamp - Measurement->EndTimeStamp;
78 Error = (BOOLEAN)(Duration > Measurement->StartTimeStamp);
79 }
80
81 if (Error) {
82 DEBUG ((EFI_D_ERROR, ALit_TimerLibError));
83 Duration = 0;
84 }
85 return Duration;
86 }
87
88 /** Determine whether the Measurement record is for an EFI Phase.
89 *
90 * The Token and Module members of the measurement record are checked.
91 * Module must be empty and Token must be one of SEC, PEI, DXE, BDS, or SHELL.
92 *
93 * @param[in] Measurement A pointer to the Measurement record to test.
94 *
95 * @retval TRUE The measurement record is for an EFI Phase.
96 * @retval FALSE The measurement record is NOT for an EFI Phase.
97 **/
98 BOOLEAN
99 IsPhase(
100 IN MEASUREMENT_RECORD *Measurement
101 )
102 {
103 BOOLEAN RetVal;
104
105 RetVal = (BOOLEAN)( ( *Measurement->Module == '\0') &&
106 ((AsciiStrnCmp (Measurement->Token, ALit_SEC, PERF_TOKEN_LENGTH) == 0) ||
107 (AsciiStrnCmp (Measurement->Token, ALit_PEI, PERF_TOKEN_LENGTH) == 0) ||
108 (AsciiStrnCmp (Measurement->Token, ALit_DXE, PERF_TOKEN_LENGTH) == 0) ||
109 (AsciiStrnCmp (Measurement->Token, ALit_BDS, PERF_TOKEN_LENGTH) == 0))
110 );
111 return RetVal;
112 }
113
114 /** Get the file name portion of the Pdb File Name.
115 *
116 * The portion of the Pdb File Name between the last backslash and
117 * either a following period or the end of the string is converted
118 * to Unicode and copied into UnicodeBuffer. The name is truncated,
119 * if necessary, to ensure that UnicodeBuffer is not overrun.
120 *
121 * @param[in] PdbFileName Pdb file name.
122 * @param[out] UnicodeBuffer The resultant Unicode File Name.
123 *
124 **/
125 VOID
126 GetShortPdbFileName (
127 IN CHAR8 *PdbFileName,
128 OUT CHAR16 *UnicodeBuffer
129 )
130 {
131 UINTN IndexA; // Current work location within an ASCII string.
132 UINTN IndexU; // Current work location within a Unicode string.
133 UINTN StartIndex;
134 UINTN EndIndex;
135
136 ZeroMem (UnicodeBuffer, DXE_PERFORMANCE_STRING_LENGTH * sizeof (CHAR16));
137
138 if (PdbFileName == NULL) {
139 StrCpy (UnicodeBuffer, L" ");
140 } else {
141 StartIndex = 0;
142 for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++)
143 ;
144 for (IndexA = 0; PdbFileName[IndexA] != 0; IndexA++) {
145 if (PdbFileName[IndexA] == '\\') {
146 StartIndex = IndexA + 1;
147 }
148
149 if (PdbFileName[IndexA] == '.') {
150 EndIndex = IndexA;
151 }
152 }
153
154 IndexU = 0;
155 for (IndexA = StartIndex; IndexA < EndIndex; IndexA++) {
156 UnicodeBuffer[IndexU] = (CHAR16) PdbFileName[IndexA];
157 IndexU++;
158 if (IndexU >= DXE_PERFORMANCE_STRING_LENGTH) {
159 UnicodeBuffer[DXE_PERFORMANCE_STRING_LENGTH] = 0;
160 break;
161 }
162 }
163 }
164 }
165
166 /** Get a human readable name for an image handle.
167 *
168 * @param[in] Handle
169 *
170 * @post The resulting Unicode name string is stored in the
171 * mGaugeString global array.
172 *
173 **/
174 VOID
175 GetNameFromHandle (
176 IN EFI_HANDLE Handle
177 )
178 {
179 EFI_STATUS Status;
180 EFI_LOADED_IMAGE_PROTOCOL *Image;
181 CHAR8 *PdbFileName;
182 EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
183 EFI_STRING StringPtr;
184
185 // Proactively get the error message so it will be ready if needed
186 StringPtr = HiiGetString (gHiiHandle, STRING_TOKEN (STR_DP_ERROR_NAME), NULL);
187 ASSERT (StringPtr != NULL);
188
189 // Get handle name from image protocol
190 //
191 Status = gBS->HandleProtocol (
192 Handle,
193 &gEfiLoadedImageProtocolGuid,
194 &Image
195 );
196
197 if (EFI_ERROR (Status)) {
198 Status = gBS->OpenProtocol (
199 Handle,
200 &gEfiDriverBindingProtocolGuid,
201 (VOID **) &DriverBinding,
202 NULL,
203 NULL,
204 EFI_OPEN_PROTOCOL_GET_PROTOCOL
205 );
206 if (EFI_ERROR (Status)) {
207 StrCpy (mGaugeString, StringPtr);
208 return ;
209 }
210
211 // Get handle name from image protocol
212 //
213 Status = gBS->HandleProtocol (
214 DriverBinding->ImageHandle,
215 &gEfiLoadedImageProtocolGuid,
216 &Image
217 );
218 }
219
220 PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase);
221
222 if (PdbFileName != NULL) {
223 GetShortPdbFileName (PdbFileName, mGaugeString);
224 } else {
225 StrCpy (mGaugeString, StringPtr);
226 }
227 return ;
228 }
229
230 /** Calculate the Duration in microseconds.
231 *
232 * Duration is multiplied by 1000, instead of Frequency being divided by 1000 or
233 * multiplying the result by 1000, in order to maintain precision. Since Duration is
234 * a 64-bit value, multiplying it by 1000 is unlikely to produce an overflow.
235 *
236 * The time is calculated as (Duration * 1000) / Timer_Frequency.
237 *
238 * @param[in] Duration The event duration in timer ticks.
239 *
240 * @return A 64-bit value which is the Elapsed time in microseconds.
241 **/
242 UINT64
243 DurationInMicroSeconds (
244 IN UINT64 Duration
245 )
246 {
247 UINT64 Temp;
248
249 Temp = MultU64x32 (Duration, 1000);
250 return DivU64x32 (Temp, TimerInfo.Frequency);
251 }
252
253 /** Formatted Print using a Hii Token to reference the localized format string.
254 *
255 * @param[in] Token A HII token associated with a localized Unicode string.
256 *
257 * @return The number of characters converted by UnicodeVSPrint().
258 *
259 **/
260 UINTN
261 PrintToken (
262 IN UINT16 Token,
263 ...
264 )
265 {
266 VA_LIST Marker;
267 EFI_STRING StringPtr;
268 UINTN Return;
269 UINTN BufferSize;
270
271 StringPtr = HiiGetString (gHiiHandle, Token, NULL);
272 ASSERT (StringPtr != NULL);
273
274 VA_START (Marker, Token);
275
276 BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16);
277
278 if (mPrintTokenBuffer == NULL) {
279 mPrintTokenBuffer = AllocatePool (BufferSize);
280 ASSERT (mPrintTokenBuffer != NULL);
281 }
282 SetMem( mPrintTokenBuffer, BufferSize, 0);
283
284 Return = UnicodeVSPrint (mPrintTokenBuffer, BufferSize, StringPtr, Marker);
285 if (Return > 0 && gST->ConOut != NULL) {
286 gST->ConOut->OutputString (gST->ConOut, mPrintTokenBuffer);
287 }
288 return Return;
289 }
290
291 /** Get index of Measurement Record's match in the CumData array.
292 *
293 * If the Measurement's Token value matches a Token in one of the CumData
294 * records, the index of the matching record is returned. The returned
295 * index is a signed value so that negative values can indicate that
296 * the Measurement didn't match any entry in the CumData array.
297 *
298 * @param[in] Measurement A pointer to a Measurement Record to match against the CumData array.
299 *
300 * @retval <0 Token is not in the CumData array.
301 * @retval >=0 Return value is the index into CumData where Token is found.
302 **/
303 INTN
304 GetCumulativeItem(
305 IN MEASUREMENT_RECORD *Measurement
306 )
307 {
308 INTN Index;
309
310 for( Index = 0; Index < (INTN)NumCum; ++Index) {
311 if (AsciiStrnCmp (Measurement->Token, CumData[Index].Name, PERF_TOKEN_LENGTH) == 0) {
312 return Index; // Exit, we found a match
313 }
314 }
315 // If the for loop exits, Token was not found.
316 return -1; // Indicate failure
317 }