]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/BuildReport.py
722d8b7f458cbec76d46412f57eda0944d191119
[mirror_edk2.git] / BaseTools / Source / Python / build / BuildReport.py
1 ## @file
2 # Routines for generating build report.
3 #
4 # This module contains the functionality to generate build report after
5 # build all target completes successfully.
6 #
7 # Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
8 # This program and the accompanying materials
9 # are licensed and made available under the terms and conditions of the BSD License
10 # which accompanies this distribution. The full text of the license may be found at
11 # http://opensource.org/licenses/bsd-license.php
12 #
13 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #
16
17 ## Import Modules
18 #
19 import Common.LongFilePathOs as os
20 import re
21 import platform
22 import textwrap
23 import traceback
24 import sys
25 import time
26 import struct
27 from datetime import datetime
28 from StringIO import StringIO
29 from Common import EdkLogger
30 from Common.Misc import SaveFileOnChange
31 from Common.Misc import GuidStructureByteArrayToGuidString
32 from Common.Misc import GuidStructureStringToGuidString
33 from Common.InfClassObject import gComponentType2ModuleType
34 from Common.BuildToolError import FILE_WRITE_FAILURE
35 from Common.BuildToolError import CODE_ERROR
36 from Common.DataType import TAB_LINE_BREAK
37 from Common.DataType import TAB_DEPEX
38 from Common.DataType import TAB_SLASH
39 from Common.DataType import TAB_SPACE_SPLIT
40 from Common.DataType import TAB_BRG_PCD
41 from Common.DataType import TAB_BRG_LIBRARY
42 from Common.DataType import TAB_BACK_SLASH
43 from Common.LongFilePathSupport import OpenLongFilePath as open
44 from Common.MultipleWorkspace import MultipleWorkspace as mws
45
46 ## Pattern to extract contents in EDK DXS files
47 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
48
49 ## Pattern to find total FV total size, occupied size in flash report intermediate file
50 gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
51 gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
52
53 ## Pattern to find module size and time stamp in module summary report intermediate file
54 gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
55 gTimeStampPattern = re.compile(r"TIME_STAMP = (\d+)")
56
57 ## Pattern to find GUID value in flash description files
58 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
59
60 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
61 gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
62
63 ## Pattern to find module base address and entry point in fixed flash map file
64 gModulePattern = r"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
65 gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
66
67 ## Pattern to find all module referenced header files in source files
68 gIncludePattern = re.compile(r'#include\s*["<]([^">]+)[">]')
69 gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
70
71 ## Pattern to find the entry point for EDK module using EDKII Glue library
72 gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
73
74 ## Tags for MaxLength of line in report
75 gLineMaxLength = 120
76
77 ## Tags for end of line in report
78 gEndOfLine = "\r\n"
79
80 ## Tags for section start, end and separator
81 gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<"
82 gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n"
83 gSectionSep = "=" * gLineMaxLength
84
85 ## Tags for subsection start, end and separator
86 gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<"
87 gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">"
88 gSubSectionSep = "-" * gLineMaxLength
89
90
91 ## The look up table to map PCD type to pair of report display type and DEC type
92 gPcdTypeMap = {
93 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'),
94 'PatchableInModule': ('PATCH', 'PatchableInModule'),
95 'FeatureFlag' : ('FLAG', 'FeatureFlag'),
96 'Dynamic' : ('DYN', 'Dynamic'),
97 'DynamicHii' : ('DYNHII', 'Dynamic'),
98 'DynamicVpd' : ('DYNVPD', 'Dynamic'),
99 'DynamicEx' : ('DEX', 'DynamicEx'),
100 'DynamicExHii' : ('DEXHII', 'DynamicEx'),
101 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'),
102 }
103
104 ## The look up table to map module type to driver type
105 gDriverTypeMap = {
106 'SEC' : '0x3 (SECURITY_CORE)',
107 'PEI_CORE' : '0x4 (PEI_CORE)',
108 'PEIM' : '0x6 (PEIM)',
109 'DXE_CORE' : '0x5 (DXE_CORE)',
110 'DXE_DRIVER' : '0x7 (DRIVER)',
111 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
112 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
113 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
114 'UEFI_DRIVER' : '0x7 (DRIVER)',
115 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
116 'SMM_CORE' : '0xD (SMM_CORE)',
117 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
118 }
119
120 ## The look up table of the supported opcode in the dependency expression binaries
121 gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
122
123 ##
124 # Writes a string to the file object.
125 #
126 # This function writes a string to the file object and a new line is appended
127 # afterwards. It may optionally wraps the string for better readability.
128 #
129 # @File The file object to write
130 # @String The string to be written to the file
131 # @Wrapper Indicates whether to wrap the string
132 #
133 def FileWrite(File, String, Wrapper=False):
134 if Wrapper:
135 String = textwrap.fill(String, 120)
136 File.write(String + gEndOfLine)
137
138 ##
139 # Find all the header file that the module source directly includes.
140 #
141 # This function scans source code to find all header files the module may
142 # include. This is not accurate but very effective to find all the header
143 # file the module might include with #include statement.
144 #
145 # @Source The source file name
146 # @IncludePathList The list of include path to find the source file.
147 # @IncludeFiles The dictionary of current found include files.
148 #
149 def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
150 FileContents = open(Source).read()
151 #
152 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
153 #
154 for Match in gIncludePattern.finditer(FileContents):
155 FileName = Match.group(1).strip()
156 for Dir in [os.path.dirname(Source)] + IncludePathList:
157 FullFileName = os.path.normpath(os.path.join(Dir, FileName))
158 if os.path.exists(FullFileName):
159 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
160 break
161
162 #
163 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
164 #
165 for Match in gIncludePattern2.finditer(FileContents):
166 Key = Match.group(2)
167 Type = Match.group(1)
168 if "ARCH_PROTOCOL" in Type:
169 FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
170 elif "PROTOCOL" in Type:
171 FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
172 elif "PPI" in Type:
173 FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
174 elif "GUID" in Type:
175 FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
176 else:
177 continue
178 for Dir in IncludePathList:
179 FullFileName = os.path.normpath(os.path.join(Dir, FileName))
180 if os.path.exists(FullFileName):
181 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
182 break
183
184 ## Split each lines in file
185 #
186 # This method is used to split the lines in file to make the length of each line
187 # less than MaxLength.
188 #
189 # @param Content The content of file
190 # @param MaxLength The Max Length of the line
191 #
192 def FileLinesSplit(Content=None, MaxLength=None):
193 ContentList = Content.split(TAB_LINE_BREAK)
194 NewContent = ''
195 NewContentList = []
196 for Line in ContentList:
197 while len(Line.rstrip()) > MaxLength:
198 LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength)
199 LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength)
200 LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength)
201 if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0:
202 LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex)
203 else:
204 LineBreakIndex = MaxLength
205 NewContentList.append(Line[:LineBreakIndex])
206 Line = Line[LineBreakIndex:]
207 if Line:
208 NewContentList.append(Line)
209 for NewLine in NewContentList:
210 NewContent += NewLine + TAB_LINE_BREAK
211
212 NewContent = NewContent.replace(TAB_LINE_BREAK, gEndOfLine).replace('\r\r\n', gEndOfLine)
213 return NewContent
214
215
216
217 ##
218 # Parse binary dependency expression section
219 #
220 # This utility class parses the dependency expression section and translate the readable
221 # GUID name and value.
222 #
223 class DepexParser(object):
224 ##
225 # Constructor function for class DepexParser
226 #
227 # This constructor function collect GUID values so that the readable
228 # GUID name can be translated.
229 #
230 # @param self The object pointer
231 # @param Wa Workspace context information
232 #
233 def __init__(self, Wa):
234 self._GuidDb = {}
235 for Pa in Wa.AutoGenObjectList:
236 for Package in Pa.PackageList:
237 for Protocol in Package.Protocols:
238 GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol])
239 self._GuidDb[GuidValue.upper()] = Protocol
240 for Ppi in Package.Ppis:
241 GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi])
242 self._GuidDb[GuidValue.upper()] = Ppi
243 for Guid in Package.Guids:
244 GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid])
245 self._GuidDb[GuidValue.upper()] = Guid
246
247 ##
248 # Parse the binary dependency expression files.
249 #
250 # This function parses the binary dependency expression file and translate it
251 # to the instruction list.
252 #
253 # @param self The object pointer
254 # @param DepexFileName The file name of binary dependency expression file.
255 #
256 def ParseDepexFile(self, DepexFileName):
257 DepexFile = open(DepexFileName, "rb")
258 DepexStatement = []
259 OpCode = DepexFile.read(1)
260 while OpCode:
261 Statement = gOpCodeList[struct.unpack("B", OpCode)[0]]
262 if Statement in ["BEFORE", "AFTER", "PUSH"]:
263 GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
264 struct.unpack("=LHHBBBBBBBB", DepexFile.read(16))
265 GuidString = self._GuidDb.get(GuidValue, GuidValue)
266 Statement = "%s %s" % (Statement, GuidString)
267 DepexStatement.append(Statement)
268 OpCode = DepexFile.read(1)
269
270 return DepexStatement
271
272 ##
273 # Reports library information
274 #
275 # This class reports the module library subsection in the build report file.
276 #
277 class LibraryReport(object):
278 ##
279 # Constructor function for class LibraryReport
280 #
281 # This constructor function generates LibraryReport object for
282 # a module.
283 #
284 # @param self The object pointer
285 # @param M Module context information
286 #
287 def __init__(self, M):
288 self.LibraryList = []
289 if int(str(M.AutoGenVersion), 0) >= 0x00010005:
290 self._EdkIIModule = True
291 else:
292 self._EdkIIModule = False
293
294 for Lib in M.DependentLibraryList:
295 LibInfPath = str(Lib)
296 LibClassList = Lib.LibraryClass[0].LibraryClass
297 LibConstructorList = Lib.ConstructorList
298 LibDesstructorList = Lib.DestructorList
299 LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
300 self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList))
301
302 ##
303 # Generate report for module library information
304 #
305 # This function generates report for the module library.
306 # If the module is EDKII style one, the additional library class, library
307 # constructor/destructor and dependency expression may also be reported.
308 #
309 # @param self The object pointer
310 # @param File The file object for report
311 #
312 def GenerateReport(self, File):
313 FileWrite(File, gSubSectionStart)
314 FileWrite(File, TAB_BRG_LIBRARY)
315 if len(self.LibraryList) > 0:
316 FileWrite(File, gSubSectionSep)
317 for LibraryItem in self.LibraryList:
318 LibInfPath = LibraryItem[0]
319 FileWrite(File, LibInfPath)
320
321 #
322 # Report library class, library constructor and destructor for
323 # EDKII style module.
324 #
325 if self._EdkIIModule:
326 LibClass = LibraryItem[1]
327 EdkIILibInfo = ""
328 LibConstructor = " ".join(LibraryItem[2])
329 if LibConstructor:
330 EdkIILibInfo += " C = " + LibConstructor
331 LibDestructor = " ".join(LibraryItem[3])
332 if LibDestructor:
333 EdkIILibInfo += " D = " + LibDestructor
334 LibDepex = " ".join(LibraryItem[4])
335 if LibDepex:
336 EdkIILibInfo += " Depex = " + LibDepex
337 if EdkIILibInfo:
338 FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
339 else:
340 FileWrite(File, "{%s}" % LibClass)
341
342 FileWrite(File, gSubSectionEnd)
343
344 ##
345 # Reports dependency expression information
346 #
347 # This class reports the module dependency expression subsection in the build report file.
348 #
349 class DepexReport(object):
350 ##
351 # Constructor function for class DepexReport
352 #
353 # This constructor function generates DepexReport object for
354 # a module. If the module source contains the DXS file (usually EDK
355 # style module), it uses the dependency in DXS file; otherwise,
356 # it uses the dependency expression from its own INF [Depex] section
357 # and then merges with the ones from its dependent library INF.
358 #
359 # @param self The object pointer
360 # @param M Module context information
361 #
362 def __init__(self, M):
363 self.Depex = ""
364 self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex")
365 ModuleType = M.ModuleType
366 if not ModuleType:
367 ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
368
369 if ModuleType in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
370 return
371
372 for Source in M.SourceFileList:
373 if os.path.splitext(Source.Path)[1].lower() == ".dxs":
374 Match = gDxsDependencyPattern.search(open(Source.Path).read())
375 if Match:
376 self.Depex = Match.group(1).strip()
377 self.Source = "DXS"
378 break
379 else:
380 self.Depex = M.DepexExpressionList.get(M.ModuleType, "")
381 self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
382 if not self.ModuleDepex:
383 self.ModuleDepex = "(None)"
384
385 LibDepexList = []
386 for Lib in M.DependentLibraryList:
387 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
388 if LibDepex != "":
389 LibDepexList.append("(" + LibDepex + ")")
390 self.LibraryDepex = " AND ".join(LibDepexList)
391 if not self.LibraryDepex:
392 self.LibraryDepex = "(None)"
393 self.Source = "INF"
394
395 ##
396 # Generate report for module dependency expression information
397 #
398 # This function generates report for the module dependency expression.
399 #
400 # @param self The object pointer
401 # @param File The file object for report
402 # @param GlobalDepexParser The platform global Dependency expression parser object
403 #
404 def GenerateReport(self, File, GlobalDepexParser):
405 if not self.Depex:
406 FileWrite(File, gSubSectionStart)
407 FileWrite(File, TAB_DEPEX)
408 FileWrite(File, gSubSectionEnd)
409 return
410 FileWrite(File, gSubSectionStart)
411 if os.path.isfile(self._DepexFileName):
412 try:
413 DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName)
414 FileWrite(File, "Final Dependency Expression (DEPEX) Instructions")
415 for DepexStatement in DepexStatements:
416 FileWrite(File, " %s" % DepexStatement)
417 FileWrite(File, gSubSectionSep)
418 except:
419 EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName)
420
421 FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
422
423 if self.Source == "INF":
424 FileWrite(File, "%s" % self.Depex, True)
425 FileWrite(File, gSubSectionSep)
426 FileWrite(File, "From Module INF: %s" % self.ModuleDepex, True)
427 FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
428 else:
429 FileWrite(File, "%s" % self.Depex)
430 FileWrite(File, gSubSectionEnd)
431
432 ##
433 # Reports dependency expression information
434 #
435 # This class reports the module build flags subsection in the build report file.
436 #
437 class BuildFlagsReport(object):
438 ##
439 # Constructor function for class BuildFlagsReport
440 #
441 # This constructor function generates BuildFlagsReport object for
442 # a module. It reports the build tool chain tag and all relevant
443 # build flags to build the module.
444 #
445 # @param self The object pointer
446 # @param M Module context information
447 #
448 def __init__(self, M):
449 BuildOptions = {}
450 #
451 # Add build flags according to source file extension so that
452 # irrelevant ones can be filtered out.
453 #
454 for Source in M.SourceFileList:
455 Ext = os.path.splitext(Source.File)[1].lower()
456 if Ext in [".c", ".cc", ".cpp"]:
457 BuildOptions["CC"] = 1
458 elif Ext in [".s", ".asm"]:
459 BuildOptions["PP"] = 1
460 BuildOptions["ASM"] = 1
461 elif Ext in [".vfr"]:
462 BuildOptions["VFRPP"] = 1
463 BuildOptions["VFR"] = 1
464 elif Ext in [".dxs"]:
465 BuildOptions["APP"] = 1
466 BuildOptions["CC"] = 1
467 elif Ext in [".asl"]:
468 BuildOptions["ASLPP"] = 1
469 BuildOptions["ASL"] = 1
470 elif Ext in [".aslc"]:
471 BuildOptions["ASLCC"] = 1
472 BuildOptions["ASLDLINK"] = 1
473 BuildOptions["CC"] = 1
474 elif Ext in [".asm16"]:
475 BuildOptions["ASMLINK"] = 1
476 BuildOptions["SLINK"] = 1
477 BuildOptions["DLINK"] = 1
478
479 #
480 # Save module build flags.
481 #
482 self.ToolChainTag = M.ToolChain
483 self.BuildFlags = {}
484 for Tool in BuildOptions:
485 self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
486
487 ##
488 # Generate report for module build flags information
489 #
490 # This function generates report for the module build flags expression.
491 #
492 # @param self The object pointer
493 # @param File The file object for report
494 #
495 def GenerateReport(self, File):
496 FileWrite(File, gSubSectionStart)
497 FileWrite(File, "Build Flags")
498 FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
499 for Tool in self.BuildFlags:
500 FileWrite(File, gSubSectionSep)
501 FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
502
503 FileWrite(File, gSubSectionEnd)
504
505
506 ##
507 # Reports individual module information
508 #
509 # This class reports the module section in the build report file.
510 # It comprises of module summary, module PCD, library, dependency expression,
511 # build flags sections.
512 #
513 class ModuleReport(object):
514 ##
515 # Constructor function for class ModuleReport
516 #
517 # This constructor function generates ModuleReport object for
518 # a separate module in a platform build.
519 #
520 # @param self The object pointer
521 # @param M Module context information
522 # @param ReportType The kind of report items in the final report file
523 #
524 def __init__(self, M, ReportType):
525 self.ModuleName = M.Module.BaseName
526 self.ModuleInfPath = M.MetaFile.File
527 self.FileGuid = M.Guid
528 self.Size = 0
529 self.BuildTimeStamp = None
530 self.DriverType = ""
531 if not M.IsLibrary:
532 ModuleType = M.ModuleType
533 if not ModuleType:
534 ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
535 #
536 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
537 #
538 if ModuleType == "DXE_SMM_DRIVER":
539 PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
540 if int(PiSpec, 0) >= 0x0001000A:
541 ModuleType = "SMM_DRIVER"
542 self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
543 self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
544 self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
545 self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
546 self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
547 self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
548
549 self._BuildDir = M.BuildDir
550 self.ModulePcdSet = {}
551 if "PCD" in ReportType:
552 #
553 # Collect all module used PCD set: module INF referenced directly or indirectly.
554 # It also saves module INF default values of them in case they exist.
555 #
556 for Pcd in M.ModulePcdList + M.LibraryPcdList:
557 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
558
559 self.LibraryReport = None
560 if "LIBRARY" in ReportType:
561 self.LibraryReport = LibraryReport(M)
562
563 self.DepexReport = None
564 if "DEPEX" in ReportType:
565 self.DepexReport = DepexReport(M)
566
567 if "BUILD_FLAGS" in ReportType:
568 self.BuildFlagsReport = BuildFlagsReport(M)
569
570
571 ##
572 # Generate report for module information
573 #
574 # This function generates report for separate module expression
575 # in a platform build.
576 #
577 # @param self The object pointer
578 # @param File The file object for report
579 # @param GlobalPcdReport The platform global PCD report object
580 # @param GlobalPredictionReport The platform global Prediction report object
581 # @param GlobalDepexParser The platform global Dependency expression parser object
582 # @param ReportType The kind of report items in the final report file
583 #
584 def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
585 FileWrite(File, gSectionStart)
586
587 FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
588 if os.path.isfile(FwReportFileName):
589 try:
590 FileContents = open(FwReportFileName).read()
591 Match = gModuleSizePattern.search(FileContents)
592 if Match:
593 self.Size = int(Match.group(1))
594
595 Match = gTimeStampPattern.search(FileContents)
596 if Match:
597 self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1)))
598 except IOError:
599 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
600
601 FileWrite(File, "Module Summary")
602 FileWrite(File, "Module Name: %s" % self.ModuleName)
603 FileWrite(File, "Module INF Path: %s" % self.ModuleInfPath)
604 FileWrite(File, "File GUID: %s" % self.FileGuid)
605 if self.Size:
606 FileWrite(File, "Size: 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
607 if self.BuildTimeStamp:
608 FileWrite(File, "Build Time Stamp: %s" % self.BuildTimeStamp)
609 if self.DriverType:
610 FileWrite(File, "Driver Type: %s" % self.DriverType)
611 if self.UefiSpecVersion:
612 FileWrite(File, "UEFI Spec Version: %s" % self.UefiSpecVersion)
613 if self.PiSpecVersion:
614 FileWrite(File, "PI Spec Version: %s" % self.PiSpecVersion)
615 if self.PciDeviceId:
616 FileWrite(File, "PCI Device ID: %s" % self.PciDeviceId)
617 if self.PciVendorId:
618 FileWrite(File, "PCI Vendor ID: %s" % self.PciVendorId)
619 if self.PciClassCode:
620 FileWrite(File, "PCI Class Code: %s" % self.PciClassCode)
621
622 FileWrite(File, gSectionSep)
623
624 if "PCD" in ReportType:
625 GlobalPcdReport.GenerateReport(File, self.ModulePcdSet)
626
627 if "LIBRARY" in ReportType:
628 self.LibraryReport.GenerateReport(File)
629
630 if "DEPEX" in ReportType:
631 self.DepexReport.GenerateReport(File, GlobalDepexParser)
632
633 if "BUILD_FLAGS" in ReportType:
634 self.BuildFlagsReport.GenerateReport(File)
635
636 if "FIXED_ADDRESS" in ReportType and self.FileGuid:
637 GlobalPredictionReport.GenerateReport(File, self.FileGuid)
638
639 FileWrite(File, gSectionEnd)
640
641 ##
642 # Reports platform and module PCD information
643 #
644 # This class reports the platform PCD section and module PCD subsection
645 # in the build report file.
646 #
647 class PcdReport(object):
648 ##
649 # Constructor function for class PcdReport
650 #
651 # This constructor function generates PcdReport object a platform build.
652 # It collects the whole PCD database from platform DSC files, platform
653 # flash description file and package DEC files.
654 #
655 # @param self The object pointer
656 # @param Wa Workspace context information
657 #
658 def __init__(self, Wa):
659 self.AllPcds = {}
660 self.MaxLen = 0
661 if Wa.FdfProfile:
662 self.FdfPcdSet = Wa.FdfProfile.PcdDict
663 else:
664 self.FdfPcdSet = {}
665
666 self.ModulePcdOverride = {}
667 for Pa in Wa.AutoGenObjectList:
668 #
669 # Collect all platform referenced PCDs and grouped them by PCD token space
670 # GUID C Names
671 #
672 for Pcd in Pa.AllPcdList:
673 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
674 if Pcd not in PcdList:
675 PcdList.append(Pcd)
676 if len(Pcd.TokenCName) > self.MaxLen:
677 self.MaxLen = len(Pcd.TokenCName)
678
679 for Module in Pa.Platform.Modules.values():
680 #
681 # Collect module override PCDs
682 #
683 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
684 TokenCName = ModulePcd.TokenCName
685 TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
686 ModuleDefault = ModulePcd.DefaultValue
687 ModulePath = os.path.basename(Module.M.MetaFile.File)
688 self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
689
690
691 #
692 # Collect PCD DEC default value.
693 #
694 self.DecPcdDefault = {}
695 for Pa in Wa.AutoGenObjectList:
696 for Package in Pa.PackageList:
697 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
698 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
699 self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
700 #
701 # Collect PCDs defined in DSC common section
702 #
703 self.DscPcdDefault = {}
704 for Arch in Wa.ArchList:
705 Platform = Wa.BuildDatabase[Wa.MetaFile, Arch, Wa.BuildTarget, Wa.ToolChain]
706 for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
707 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
708 if DscDefaultValue:
709 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
710
711 ##
712 # Generate report for PCD information
713 #
714 # This function generates report for separate module expression
715 # in a platform build.
716 #
717 # @param self The object pointer
718 # @param File The file object for report
719 # @param ModulePcdSet Set of all PCDs referenced by module or None for
720 # platform PCD report
721 # @param DscOverridePcds Module DSC override PCDs set
722 #
723 def GenerateReport(self, File, ModulePcdSet):
724 if ModulePcdSet == None:
725 #
726 # For platform global PCD section
727 #
728 FileWrite(File, gSectionStart)
729 FileWrite(File, "Platform Configuration Database Report")
730 FileWrite(File, " *P - Platform scoped PCD override in DSC file")
731 FileWrite(File, " *F - Platform scoped PCD override in FDF file")
732 FileWrite(File, " *M - Module scoped PCD override")
733 FileWrite(File, gSectionSep)
734 else:
735 #
736 # For module PCD sub-section
737 #
738 FileWrite(File, gSubSectionStart)
739 FileWrite(File, TAB_BRG_PCD)
740 FileWrite(File, gSubSectionSep)
741
742 for Key in self.AllPcds:
743 #
744 # Group PCD by their token space GUID C Name
745 #
746 First = True
747 for Type in self.AllPcds[Key]:
748 #
749 # Group PCD by their usage type
750 #
751 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
752 for Pcd in self.AllPcds[Key][Type]:
753 #
754 # Get PCD default value and their override relationship
755 #
756 DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
757 DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
758 DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue)
759 InfDefaultValue = None
760
761 PcdValue = DecDefaultValue
762 if DscDefaultValue:
763 PcdValue = DscDefaultValue
764 if ModulePcdSet != None:
765 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
766 continue
767 InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
768 if InfDefault == "":
769 InfDefault = None
770 if First:
771 if ModulePcdSet == None:
772 FileWrite(File, "")
773 FileWrite(File, Key)
774 First = False
775
776
777 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
778 PcdValueNumber = int(PcdValue.strip(), 0)
779 if DecDefaultValue == None:
780 DecMatch = True
781 else:
782 DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
783 DecMatch = (DecDefaultValueNumber == PcdValueNumber)
784
785 if InfDefaultValue == None:
786 InfMatch = True
787 else:
788 InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
789 InfMatch = (InfDefaultValueNumber == PcdValueNumber)
790
791 if DscDefaultValue == None:
792 DscMatch = True
793 else:
794 DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
795 DscMatch = (DscDefaultValueNumber == PcdValueNumber)
796 else:
797 if DecDefaultValue == None:
798 DecMatch = True
799 else:
800 DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
801
802 if InfDefaultValue == None:
803 InfMatch = True
804 else:
805 InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
806
807 if DscDefaultValue == None:
808 DscMatch = True
809 else:
810 DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
811
812 #
813 # Report PCD item according to their override relationship
814 #
815 if DecMatch and InfMatch:
816 FileWrite(File, ' %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
817 else:
818 if DscMatch:
819 if (Pcd.TokenCName, Key) in self.FdfPcdSet:
820 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
821 else:
822 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
823 else:
824 FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
825
826 if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
827 for SkuInfo in Pcd.SkuInfoList.values():
828 if TypeName in ('DYNHII', 'DEXHII'):
829 FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
830 else:
831 FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
832
833 if not DscMatch and DscDefaultValue != None:
834 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue.strip()))
835
836 if not InfMatch and InfDefaultValue != None:
837 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
838
839 if not DecMatch and DecDefaultValue != None:
840 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
841
842 if ModulePcdSet == None:
843 ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
844 for ModulePath in ModuleOverride:
845 ModuleDefault = ModuleOverride[ModulePath]
846 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
847 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
848 Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
849 else:
850 Match = (ModuleDefault.strip() == PcdValue.strip())
851 if Match:
852 continue
853 FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
854
855 if ModulePcdSet == None:
856 FileWrite(File, gSectionEnd)
857 else:
858 FileWrite(File, gSubSectionEnd)
859
860
861
862 ##
863 # Reports platform and module Prediction information
864 #
865 # This class reports the platform execution order prediction section and
866 # module load fixed address prediction subsection in the build report file.
867 #
868 class PredictionReport(object):
869 ##
870 # Constructor function for class PredictionReport
871 #
872 # This constructor function generates PredictionReport object for the platform.
873 #
874 # @param self: The object pointer
875 # @param Wa Workspace context information
876 #
877 def __init__(self, Wa):
878 self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
879 self._MapFileParsed = False
880 self._EotToolInvoked = False
881 self._FvDir = Wa.FvDir
882 self._EotDir = Wa.BuildDir
883 self._FfsEntryPoint = {}
884 self._GuidMap = {}
885 self._SourceList = []
886 self.FixedMapDict = {}
887 self.ItemList = []
888 self.MaxLen = 0
889
890 #
891 # Collect all platform reference source files and GUID C Name
892 #
893 for Pa in Wa.AutoGenObjectList:
894 for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
895 #
896 # BASE typed modules are EFI agnostic, so we need not scan
897 # their source code to find PPI/Protocol produce or consume
898 # information.
899 #
900 if Module.ModuleType == "BASE":
901 continue
902 #
903 # Add module referenced source files
904 #
905 self._SourceList.append(str(Module))
906 IncludeList = {}
907 for Source in Module.SourceFileList:
908 if os.path.splitext(str(Source))[1].lower() == ".c":
909 self._SourceList.append(" " + str(Source))
910 FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
911 for IncludeFile in IncludeList.values():
912 self._SourceList.append(" " + IncludeFile)
913
914 for Guid in Module.PpiList:
915 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
916 for Guid in Module.ProtocolList:
917 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
918 for Guid in Module.GuidList:
919 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
920
921 if Module.Guid and not Module.IsLibrary:
922 EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
923 if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
924 RealEntryPoint = "_ModuleEntryPoint"
925 else:
926 RealEntryPoint = EntryPoint
927 if EntryPoint == "_ModuleEntryPoint":
928 CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
929 Match = gGlueLibEntryPoint.search(CCFlags)
930 if Match:
931 EntryPoint = Match.group(1)
932
933 self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
934
935
936 #
937 # Collect platform firmware volume list as the input of EOT.
938 #
939 self._FvList = []
940 if Wa.FdfProfile:
941 for Fd in Wa.FdfProfile.FdDict:
942 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
943 if FdRegion.RegionType != "FV":
944 continue
945 for FvName in FdRegion.RegionDataList:
946 if FvName in self._FvList:
947 continue
948 self._FvList.append(FvName)
949 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
950 for Section in Ffs.SectionList:
951 try:
952 for FvSection in Section.SectionList:
953 if FvSection.FvName in self._FvList:
954 continue
955 self._FvList.append(FvSection.FvName)
956 except AttributeError:
957 pass
958
959
960 ##
961 # Parse platform fixed address map files
962 #
963 # This function parses the platform final fixed address map file to get
964 # the database of predicted fixed address for module image base, entry point
965 # etc.
966 #
967 # @param self: The object pointer
968 #
969 def _ParseMapFile(self):
970 if self._MapFileParsed:
971 return
972 self._MapFileParsed = True
973 if os.path.isfile(self._MapFileName):
974 try:
975 FileContents = open(self._MapFileName).read()
976 for Match in gMapFileItemPattern.finditer(FileContents):
977 AddressType = Match.group(1)
978 BaseAddress = Match.group(2)
979 EntryPoint = Match.group(3)
980 Guid = Match.group(4).upper()
981 List = self.FixedMapDict.setdefault(Guid, [])
982 List.append((AddressType, BaseAddress, "*I"))
983 List.append((AddressType, EntryPoint, "*E"))
984 except:
985 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
986
987 ##
988 # Invokes EOT tool to get the predicted the execution order.
989 #
990 # This function invokes EOT tool to calculate the predicted dispatch order
991 #
992 # @param self: The object pointer
993 #
994 def _InvokeEotTool(self):
995 if self._EotToolInvoked:
996 return
997
998 self._EotToolInvoked = True
999 FvFileList = []
1000 for FvName in self._FvList:
1001 FvFile = os.path.join(self._FvDir, FvName + ".Fv")
1002 if os.path.isfile(FvFile):
1003 FvFileList.append(FvFile)
1004
1005 if len(FvFileList) == 0:
1006 return
1007 #
1008 # Write source file list and GUID file list to an intermediate file
1009 # as the input for EOT tool and dispatch List as the output file
1010 # from EOT tool.
1011 #
1012 SourceList = os.path.join(self._EotDir, "SourceFile.txt")
1013 GuidList = os.path.join(self._EotDir, "GuidList.txt")
1014 DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
1015
1016 TempFile = open(SourceList, "w+")
1017 for Item in self._SourceList:
1018 FileWrite(TempFile, Item)
1019 TempFile.close()
1020 TempFile = open(GuidList, "w+")
1021 for Key in self._GuidMap:
1022 FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
1023 TempFile.close()
1024
1025 try:
1026 from Eot.Eot import Eot
1027
1028 #
1029 # Invoke EOT tool and echo its runtime performance
1030 #
1031 EotStartTime = time.time()
1032 Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
1033 FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
1034 EotEndTime = time.time()
1035 EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
1036 EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
1037
1038 #
1039 # Parse the output of EOT tool
1040 #
1041 for Line in open(DispatchList):
1042 if len(Line.split()) < 4:
1043 continue
1044 (Guid, Phase, FfsName, FilePath) = Line.split()
1045 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
1046 if len(Symbol) > self.MaxLen:
1047 self.MaxLen = len(Symbol)
1048 self.ItemList.append((Phase, Symbol, FilePath))
1049 except:
1050 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1051 EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1052
1053
1054 ##
1055 # Generate platform execution order report
1056 #
1057 # This function generates the predicted module execution order.
1058 #
1059 # @param self The object pointer
1060 # @param File The file object for report
1061 #
1062 def _GenerateExecutionOrderReport(self, File):
1063 self._InvokeEotTool()
1064 if len(self.ItemList) == 0:
1065 return
1066 FileWrite(File, gSectionStart)
1067 FileWrite(File, "Execution Order Prediction")
1068 FileWrite(File, "*P PEI phase")
1069 FileWrite(File, "*D DXE phase")
1070 FileWrite(File, "*E Module INF entry point name")
1071 FileWrite(File, "*N Module notification function name")
1072
1073 FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
1074 FileWrite(File, gSectionSep)
1075 for Item in self.ItemList:
1076 FileWrite(File, "*%sE %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
1077
1078 FileWrite(File, gSectionStart)
1079
1080 ##
1081 # Generate Fixed Address report.
1082 #
1083 # This function generate the predicted fixed address report for a module
1084 # specified by Guid.
1085 #
1086 # @param self The object pointer
1087 # @param File The file object for report
1088 # @param Guid The module Guid value.
1089 # @param NotifyList The list of all notify function in a module
1090 #
1091 def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
1092 self._ParseMapFile()
1093 FixedAddressList = self.FixedMapDict.get(Guid)
1094 if not FixedAddressList:
1095 return
1096
1097 FileWrite(File, gSubSectionStart)
1098 FileWrite(File, "Fixed Address Prediction")
1099 FileWrite(File, "*I Image Loading Address")
1100 FileWrite(File, "*E Entry Point Address")
1101 FileWrite(File, "*N Notification Function Address")
1102 FileWrite(File, "*F Flash Address")
1103 FileWrite(File, "*M Memory Address")
1104 FileWrite(File, "*S SMM RAM Offset")
1105 FileWrite(File, "TOM Top of Memory")
1106
1107 FileWrite(File, "Type Address Name")
1108 FileWrite(File, gSubSectionSep)
1109 for Item in FixedAddressList:
1110 Type = Item[0]
1111 Value = Item[1]
1112 Symbol = Item[2]
1113 if Symbol == "*I":
1114 Name = "(Image Base)"
1115 elif Symbol == "*E":
1116 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
1117 elif Symbol in NotifyList:
1118 Name = Symbol
1119 Symbol = "*N"
1120 else:
1121 continue
1122
1123 if "Flash" in Type:
1124 Symbol += "F"
1125 elif "Memory" in Type:
1126 Symbol += "M"
1127 else:
1128 Symbol += "S"
1129
1130 if Value[0] == "-":
1131 Value = "TOM" + Value
1132
1133 FileWrite(File, "%s %-16s %s" % (Symbol, Value, Name))
1134
1135 ##
1136 # Generate report for the prediction part
1137 #
1138 # This function generate the predicted fixed address report for a module or
1139 # predicted module execution order for a platform.
1140 # If the input Guid is None, then, it generates the predicted module execution order;
1141 # otherwise it generated the module fixed loading address for the module specified by
1142 # Guid.
1143 #
1144 # @param self The object pointer
1145 # @param File The file object for report
1146 # @param Guid The module Guid value.
1147 #
1148 def GenerateReport(self, File, Guid):
1149 if Guid:
1150 self._GenerateFixedAddressReport(File, Guid.upper(), [])
1151 else:
1152 self._GenerateExecutionOrderReport(File)
1153
1154 ##
1155 # Reports FD region information
1156 #
1157 # This class reports the FD subsection in the build report file.
1158 # It collects region information of platform flash device.
1159 # If the region is a firmware volume, it lists the set of modules
1160 # and its space information; otherwise, it only lists its region name,
1161 # base address and size in its sub-section header.
1162 # If there are nesting FVs, the nested FVs will list immediate after
1163 # this FD region subsection
1164 #
1165 class FdRegionReport(object):
1166 ##
1167 # Discover all the nested FV name list.
1168 #
1169 # This is an internal worker function to discover the all the nested FV information
1170 # in the parent firmware volume. It uses deep first search algorithm recursively to
1171 # find all the FV list name and append them to the list.
1172 #
1173 # @param self The object pointer
1174 # @param FvName The name of current firmware file system
1175 # @param Wa Workspace context information
1176 #
1177 def _DiscoverNestedFvList(self, FvName, Wa):
1178 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1179 for Section in Ffs.SectionList:
1180 try:
1181 for FvSection in Section.SectionList:
1182 if FvSection.FvName in self.FvList:
1183 continue
1184 self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1185 self.FvList.append(FvSection.FvName)
1186 self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1187 self._DiscoverNestedFvList(FvSection.FvName, Wa)
1188 except AttributeError:
1189 pass
1190
1191 ##
1192 # Constructor function for class FdRegionReport
1193 #
1194 # This constructor function generates FdRegionReport object for a specified FdRegion.
1195 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1196 # volume list. This function also collects GUID map in order to dump module identification
1197 # in the final report.
1198 #
1199 # @param self: The object pointer
1200 # @param FdRegion The current FdRegion object
1201 # @param Wa Workspace context information
1202 #
1203 def __init__(self, FdRegion, Wa):
1204 self.Type = FdRegion.RegionType
1205 self.BaseAddress = FdRegion.Offset
1206 self.Size = FdRegion.Size
1207 self.FvList = []
1208 self.FvInfo = {}
1209 self._GuidsDb = {}
1210 self._FvDir = Wa.FvDir
1211
1212 #
1213 # If the input FdRegion is not a firmware volume,
1214 # we are done.
1215 #
1216 if self.Type != "FV":
1217 return
1218
1219 #
1220 # Find all nested FVs in the FdRegion
1221 #
1222 for FvName in FdRegion.RegionDataList:
1223 if FvName in self.FvList:
1224 continue
1225 self.FvList.append(FvName)
1226 self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1227 self._DiscoverNestedFvList(FvName, Wa)
1228
1229 PlatformPcds = {}
1230 #
1231 # Collect PCDs declared in DEC files.
1232 #
1233 for Pa in Wa.AutoGenObjectList:
1234 for Package in Pa.PackageList:
1235 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1236 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1237 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1238 #
1239 # Collect PCDs defined in DSC file
1240 #
1241 for arch in Wa.ArchList:
1242 Platform = Wa.BuildDatabase[Wa.MetaFile, arch]
1243 for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1244 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1245 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1246
1247 #
1248 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1249 #
1250 self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1251 self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1252 #
1253 # Add ACPI table storage file
1254 #
1255 self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1256
1257 for Pa in Wa.AutoGenObjectList:
1258 for ModuleKey in Pa.Platform.Modules:
1259 M = Pa.Platform.Modules[ModuleKey].M
1260 InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1261 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1262
1263 #
1264 # Collect the GUID map in the FV firmware volume
1265 #
1266 for FvName in self.FvList:
1267 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1268 try:
1269 #
1270 # collect GUID map for binary EFI file in FDF file.
1271 #
1272 Guid = Ffs.NameGuid.upper()
1273 Match = gPcdGuidPattern.match(Ffs.NameGuid)
1274 if Match:
1275 PcdTokenspace = Match.group(1)
1276 PcdToken = Match.group(2)
1277 if (PcdToken, PcdTokenspace) in PlatformPcds:
1278 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1279 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1280 for Section in Ffs.SectionList:
1281 try:
1282 ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1283 self._GuidsDb[Guid] = ModuleSectFile
1284 except AttributeError:
1285 pass
1286 except AttributeError:
1287 pass
1288
1289
1290 ##
1291 # Internal worker function to generate report for the FD region
1292 #
1293 # This internal worker function to generate report for the FD region.
1294 # It the type is firmware volume, it lists offset and module identification.
1295 #
1296 # @param self The object pointer
1297 # @param File The file object for report
1298 # @param Title The title for the FD subsection
1299 # @param BaseAddress The base address for the FD region
1300 # @param Size The size of the FD region
1301 # @param FvName The FV name if the FD region is a firmware volume
1302 #
1303 def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1304 FileWrite(File, gSubSectionStart)
1305 FileWrite(File, Title)
1306 FileWrite(File, "Type: %s" % Type)
1307 FileWrite(File, "Base Address: 0x%X" % BaseAddress)
1308
1309 if self.Type == "FV":
1310 FvTotalSize = 0
1311 FvTakenSize = 0
1312 FvFreeSize = 0
1313 FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt")
1314 try:
1315 #
1316 # Collect size info in the firmware volume.
1317 #
1318 FvReport = open(FvReportFileName).read()
1319 Match = gFvTotalSizePattern.search(FvReport)
1320 if Match:
1321 FvTotalSize = int(Match.group(1), 16)
1322 Match = gFvTakenSizePattern.search(FvReport)
1323 if Match:
1324 FvTakenSize = int(Match.group(1), 16)
1325 FvFreeSize = FvTotalSize - FvTakenSize
1326 #
1327 # Write size information to the report file.
1328 #
1329 FileWrite(File, "Size: 0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1330 FileWrite(File, "Fv Name: %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1331 FileWrite(File, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1332 FileWrite(File, "Free Size: 0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1333 FileWrite(File, "Offset Module")
1334 FileWrite(File, gSubSectionSep)
1335 #
1336 # Write module offset and module identification to the report file.
1337 #
1338 OffsetInfo = {}
1339 for Match in gOffsetGuidPattern.finditer(FvReport):
1340 Guid = Match.group(2).upper()
1341 OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1342 OffsetList = OffsetInfo.keys()
1343 OffsetList.sort()
1344 for Offset in OffsetList:
1345 FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1346 except IOError:
1347 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1348 else:
1349 FileWrite(File, "Size: 0x%X (%.0fK)" % (Size, Size / 1024.0))
1350 FileWrite(File, gSubSectionEnd)
1351
1352 ##
1353 # Generate report for the FD region
1354 #
1355 # This function generates report for the FD region.
1356 #
1357 # @param self The object pointer
1358 # @param File The file object for report
1359 #
1360 def GenerateReport(self, File):
1361 if (len(self.FvList) > 0):
1362 for FvItem in self.FvList:
1363 Info = self.FvInfo[FvItem]
1364 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1365 else:
1366 self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1367
1368 ##
1369 # Reports FD information
1370 #
1371 # This class reports the FD section in the build report file.
1372 # It collects flash device information for a platform.
1373 #
1374 class FdReport(object):
1375 ##
1376 # Constructor function for class FdReport
1377 #
1378 # This constructor function generates FdReport object for a specified
1379 # firmware device.
1380 #
1381 # @param self The object pointer
1382 # @param Fd The current Firmware device object
1383 # @param Wa Workspace context information
1384 #
1385 def __init__(self, Fd, Wa):
1386 self.FdName = Fd.FdUiName
1387 self.BaseAddress = Fd.BaseAddress
1388 self.Size = Fd.Size
1389 self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1390 self.FvPath = os.path.join(Wa.BuildDir, "FV")
1391 self.VpdFilePath = os.path.join(self.FvPath, "%s.map" % Wa.Platform.VpdToolGuid)
1392 VpdPcdToken = 'gEfiMdeModulePkgTokenSpaceGuid'
1393 VpdPcdName = 'PcdVpdBaseAddress'
1394 self.VPDInfoList = []
1395 for index, FdRegion in enumerate(Fd.RegionList):
1396 if (VpdPcdName, VpdPcdToken) == FdRegion.PcdOffset:
1397 self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
1398 self.VPDSize = self.FdRegionList[index].Size
1399 break
1400
1401 if os.path.isfile(self.VpdFilePath):
1402 fd = open(self.VpdFilePath, "r")
1403 Lines = fd.readlines()
1404 for Line in Lines:
1405 Line = Line.strip()
1406 if len(Line) == 0 or Line.startswith("#"):
1407 continue
1408 try:
1409 PcdName, SkuId, Offset, Size, Value = Line.split("#")[0].split("|")
1410 PcdName, SkuId, Offset, Size, Value = PcdName.strip(), SkuId.strip(), Offset.strip(), Size.strip(), Value.strip()
1411 Offset = '0x%08X' % (int(Offset, 16) + self.VPDBaseAddress)
1412 self.VPDInfoList.append("%s | %s | %s | %s | %s" % (PcdName, SkuId, Offset, Size, Value))
1413 except:
1414 EdkLogger.error("BuildReport", CODE_ERROR, "Fail to parse VPD information file %s" % self.VpdFilePath)
1415 fd.close()
1416
1417 ##
1418 # Generate report for the firmware device.
1419 #
1420 # This function generates report for the firmware device.
1421 #
1422 # @param self The object pointer
1423 # @param File The file object for report
1424 #
1425 def GenerateReport(self, File):
1426 FileWrite(File, gSectionStart)
1427 FileWrite(File, "Firmware Device (FD)")
1428 FileWrite(File, "FD Name: %s" % self.FdName)
1429 FileWrite(File, "Base Address: %s" % self.BaseAddress)
1430 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1431 if len(self.FdRegionList) > 0:
1432 FileWrite(File, gSectionSep)
1433 for FdRegionItem in self.FdRegionList:
1434 FdRegionItem.GenerateReport(File)
1435
1436 if len(self.VPDInfoList) > 0:
1437 FileWrite(File, gSubSectionStart)
1438 FileWrite(File, "FD VPD Region")
1439 FileWrite(File, "Base Address: 0x%X" % self.VPDBaseAddress)
1440 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
1441 FileWrite(File, gSubSectionSep)
1442 for item in self.VPDInfoList:
1443 FileWrite(File, item)
1444 FileWrite(File, gSubSectionEnd)
1445 FileWrite(File, gSectionEnd)
1446
1447
1448
1449 ##
1450 # Reports platform information
1451 #
1452 # This class reports the whole platform information
1453 #
1454 class PlatformReport(object):
1455 ##
1456 # Constructor function for class PlatformReport
1457 #
1458 # This constructor function generates PlatformReport object a platform build.
1459 # It generates report for platform summary, flash, global PCDs and detailed
1460 # module information for modules involved in platform build.
1461 #
1462 # @param self The object pointer
1463 # @param Wa Workspace context information
1464 # @param MaList The list of modules in the platform build
1465 #
1466 def __init__(self, Wa, MaList, ReportType):
1467 self._WorkspaceDir = Wa.WorkspaceDir
1468 self.PlatformName = Wa.Name
1469 self.PlatformDscPath = Wa.Platform
1470 self.Architectures = " ".join(Wa.ArchList)
1471 self.ToolChain = Wa.ToolChain
1472 self.Target = Wa.BuildTarget
1473 self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1474 self.BuildEnvironment = platform.platform()
1475
1476 self.PcdReport = None
1477 if "PCD" in ReportType:
1478 self.PcdReport = PcdReport(Wa)
1479
1480 self.FdReportList = []
1481 if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1482 for Fd in Wa.FdfProfile.FdDict:
1483 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1484
1485 self.PredictionReport = None
1486 if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1487 self.PredictionReport = PredictionReport(Wa)
1488
1489 self.DepexParser = None
1490 if "DEPEX" in ReportType:
1491 self.DepexParser = DepexParser(Wa)
1492
1493 self.ModuleReportList = []
1494 if MaList != None:
1495 self._IsModuleBuild = True
1496 for Ma in MaList:
1497 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1498 else:
1499 self._IsModuleBuild = False
1500 for Pa in Wa.AutoGenObjectList:
1501 for ModuleKey in Pa.Platform.Modules:
1502 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, ReportType))
1503
1504
1505
1506 ##
1507 # Generate report for the whole platform.
1508 #
1509 # This function generates report for platform information.
1510 # It comprises of platform summary, global PCD, flash and
1511 # module list sections.
1512 #
1513 # @param self The object pointer
1514 # @param File The file object for report
1515 # @param BuildDuration The total time to build the modules
1516 # @param ReportType The kind of report items in the final report file
1517 #
1518 def GenerateReport(self, File, BuildDuration, ReportType):
1519 FileWrite(File, "Platform Summary")
1520 FileWrite(File, "Platform Name: %s" % self.PlatformName)
1521 FileWrite(File, "Platform DSC Path: %s" % self.PlatformDscPath)
1522 FileWrite(File, "Architectures: %s" % self.Architectures)
1523 FileWrite(File, "Tool Chain: %s" % self.ToolChain)
1524 FileWrite(File, "Target: %s" % self.Target)
1525 FileWrite(File, "Output Path: %s" % self.OutputPath)
1526 FileWrite(File, "Build Environment: %s" % self.BuildEnvironment)
1527 FileWrite(File, "Build Duration: %s" % BuildDuration)
1528 FileWrite(File, "Report Content: %s" % ", ".join(ReportType))
1529
1530 if not self._IsModuleBuild:
1531 if "PCD" in ReportType:
1532 self.PcdReport.GenerateReport(File, None)
1533
1534 if "FLASH" in ReportType:
1535 for FdReportListItem in self.FdReportList:
1536 FdReportListItem.GenerateReport(File)
1537
1538 for ModuleReportItem in self.ModuleReportList:
1539 ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
1540
1541 if not self._IsModuleBuild:
1542 if "EXECUTION_ORDER" in ReportType:
1543 self.PredictionReport.GenerateReport(File, None)
1544
1545 ## BuildReport class
1546 #
1547 # This base class contain the routines to collect data and then
1548 # applies certain format to the output report
1549 #
1550 class BuildReport(object):
1551 ##
1552 # Constructor function for class BuildReport
1553 #
1554 # This constructor function generates BuildReport object a platform build.
1555 # It generates report for platform summary, flash, global PCDs and detailed
1556 # module information for modules involved in platform build.
1557 #
1558 # @param self The object pointer
1559 # @param ReportFile The file name to save report file
1560 # @param ReportType The kind of report items in the final report file
1561 #
1562 def __init__(self, ReportFile, ReportType):
1563 self.ReportFile = ReportFile
1564 if ReportFile:
1565 self.ReportList = []
1566 self.ReportType = []
1567 if ReportType:
1568 for ReportTypeItem in ReportType:
1569 if ReportTypeItem not in self.ReportType:
1570 self.ReportType.append(ReportTypeItem)
1571 else:
1572 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1573 ##
1574 # Adds platform report to the list
1575 #
1576 # This function adds a platform report to the final report list.
1577 #
1578 # @param self The object pointer
1579 # @param Wa Workspace context information
1580 # @param MaList The list of modules in the platform build
1581 #
1582 def AddPlatformReport(self, Wa, MaList=None):
1583 if self.ReportFile:
1584 self.ReportList.append((Wa, MaList))
1585
1586 ##
1587 # Generates the final report.
1588 #
1589 # This function generates platform build report. It invokes GenerateReport()
1590 # method for every platform report in the list.
1591 #
1592 # @param self The object pointer
1593 # @param BuildDuration The total time to build the modules
1594 #
1595 def GenerateReport(self, BuildDuration):
1596 if self.ReportFile:
1597 try:
1598 File = StringIO('')
1599 for (Wa, MaList) in self.ReportList:
1600 PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType)
1601 Content = FileLinesSplit(File.getvalue(), gLineMaxLength)
1602 SaveFileOnChange(self.ReportFile, Content, True)
1603 EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
1604 except IOError:
1605 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1606 except:
1607 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1608 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1609 File.close()
1610
1611 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1612 if __name__ == '__main__':
1613 pass
1614