]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/BuildReport.py
BaseTools: update the mail address for stack trace info
[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 - 2016, 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 FvDictKey=FvName.upper()
1179 if FvDictKey in Wa.FdfProfile.FvDict:
1180 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1181 for Section in Ffs.SectionList:
1182 try:
1183 for FvSection in Section.SectionList:
1184 if FvSection.FvName in self.FvList:
1185 continue
1186 self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1187 self.FvList.append(FvSection.FvName)
1188 self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1189 self._DiscoverNestedFvList(FvSection.FvName, Wa)
1190 except AttributeError:
1191 pass
1192
1193 ##
1194 # Constructor function for class FdRegionReport
1195 #
1196 # This constructor function generates FdRegionReport object for a specified FdRegion.
1197 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1198 # volume list. This function also collects GUID map in order to dump module identification
1199 # in the final report.
1200 #
1201 # @param self: The object pointer
1202 # @param FdRegion The current FdRegion object
1203 # @param Wa Workspace context information
1204 #
1205 def __init__(self, FdRegion, Wa):
1206 self.Type = FdRegion.RegionType
1207 self.BaseAddress = FdRegion.Offset
1208 self.Size = FdRegion.Size
1209 self.FvList = []
1210 self.FvInfo = {}
1211 self._GuidsDb = {}
1212 self._FvDir = Wa.FvDir
1213
1214 #
1215 # If the input FdRegion is not a firmware volume,
1216 # we are done.
1217 #
1218 if self.Type != "FV":
1219 return
1220
1221 #
1222 # Find all nested FVs in the FdRegion
1223 #
1224 for FvName in FdRegion.RegionDataList:
1225 if FvName in self.FvList:
1226 continue
1227 self.FvList.append(FvName)
1228 self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1229 self._DiscoverNestedFvList(FvName, Wa)
1230
1231 PlatformPcds = {}
1232 #
1233 # Collect PCDs declared in DEC files.
1234 #
1235 for Pa in Wa.AutoGenObjectList:
1236 for Package in Pa.PackageList:
1237 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1238 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1239 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1240 #
1241 # Collect PCDs defined in DSC file
1242 #
1243 for arch in Wa.ArchList:
1244 Platform = Wa.BuildDatabase[Wa.MetaFile, arch]
1245 for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1246 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1247 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1248
1249 #
1250 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1251 #
1252 self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1253 self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1254 #
1255 # Add ACPI table storage file
1256 #
1257 self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1258
1259 for Pa in Wa.AutoGenObjectList:
1260 for ModuleKey in Pa.Platform.Modules:
1261 M = Pa.Platform.Modules[ModuleKey].M
1262 InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1263 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1264
1265 #
1266 # Collect the GUID map in the FV firmware volume
1267 #
1268 for FvName in self.FvList:
1269 FvDictKey=FvName.upper()
1270 if FvDictKey in Wa.FdfProfile.FvDict:
1271 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1272 try:
1273 #
1274 # collect GUID map for binary EFI file in FDF file.
1275 #
1276 Guid = Ffs.NameGuid.upper()
1277 Match = gPcdGuidPattern.match(Ffs.NameGuid)
1278 if Match:
1279 PcdTokenspace = Match.group(1)
1280 PcdToken = Match.group(2)
1281 if (PcdToken, PcdTokenspace) in PlatformPcds:
1282 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1283 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1284 for Section in Ffs.SectionList:
1285 try:
1286 ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1287 self._GuidsDb[Guid] = ModuleSectFile
1288 except AttributeError:
1289 pass
1290 except AttributeError:
1291 pass
1292
1293
1294 ##
1295 # Internal worker function to generate report for the FD region
1296 #
1297 # This internal worker function to generate report for the FD region.
1298 # It the type is firmware volume, it lists offset and module identification.
1299 #
1300 # @param self The object pointer
1301 # @param File The file object for report
1302 # @param Title The title for the FD subsection
1303 # @param BaseAddress The base address for the FD region
1304 # @param Size The size of the FD region
1305 # @param FvName The FV name if the FD region is a firmware volume
1306 #
1307 def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1308 FileWrite(File, gSubSectionStart)
1309 FileWrite(File, Title)
1310 FileWrite(File, "Type: %s" % Type)
1311 FileWrite(File, "Base Address: 0x%X" % BaseAddress)
1312
1313 if self.Type == "FV":
1314 FvTotalSize = 0
1315 FvTakenSize = 0
1316 FvFreeSize = 0
1317 FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt")
1318 try:
1319 #
1320 # Collect size info in the firmware volume.
1321 #
1322 FvReport = open(FvReportFileName).read()
1323 Match = gFvTotalSizePattern.search(FvReport)
1324 if Match:
1325 FvTotalSize = int(Match.group(1), 16)
1326 Match = gFvTakenSizePattern.search(FvReport)
1327 if Match:
1328 FvTakenSize = int(Match.group(1), 16)
1329 FvFreeSize = FvTotalSize - FvTakenSize
1330 #
1331 # Write size information to the report file.
1332 #
1333 FileWrite(File, "Size: 0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1334 FileWrite(File, "Fv Name: %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1335 FileWrite(File, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1336 FileWrite(File, "Free Size: 0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1337 FileWrite(File, "Offset Module")
1338 FileWrite(File, gSubSectionSep)
1339 #
1340 # Write module offset and module identification to the report file.
1341 #
1342 OffsetInfo = {}
1343 for Match in gOffsetGuidPattern.finditer(FvReport):
1344 Guid = Match.group(2).upper()
1345 OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1346 OffsetList = OffsetInfo.keys()
1347 OffsetList.sort()
1348 for Offset in OffsetList:
1349 FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1350 except IOError:
1351 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1352 else:
1353 FileWrite(File, "Size: 0x%X (%.0fK)" % (Size, Size / 1024.0))
1354 FileWrite(File, gSubSectionEnd)
1355
1356 ##
1357 # Generate report for the FD region
1358 #
1359 # This function generates report for the FD region.
1360 #
1361 # @param self The object pointer
1362 # @param File The file object for report
1363 #
1364 def GenerateReport(self, File):
1365 if (len(self.FvList) > 0):
1366 for FvItem in self.FvList:
1367 Info = self.FvInfo[FvItem]
1368 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1369 else:
1370 self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1371
1372 ##
1373 # Reports FD information
1374 #
1375 # This class reports the FD section in the build report file.
1376 # It collects flash device information for a platform.
1377 #
1378 class FdReport(object):
1379 ##
1380 # Constructor function for class FdReport
1381 #
1382 # This constructor function generates FdReport object for a specified
1383 # firmware device.
1384 #
1385 # @param self The object pointer
1386 # @param Fd The current Firmware device object
1387 # @param Wa Workspace context information
1388 #
1389 def __init__(self, Fd, Wa):
1390 self.FdName = Fd.FdUiName
1391 self.BaseAddress = Fd.BaseAddress
1392 self.Size = Fd.Size
1393 self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1394 self.FvPath = os.path.join(Wa.BuildDir, "FV")
1395 self.VpdFilePath = os.path.join(self.FvPath, "%s.map" % Wa.Platform.VpdToolGuid)
1396 self.VPDBaseAddress = 0
1397 self.VPDSize = 0
1398 self.VPDInfoList = []
1399 for index, FdRegion in enumerate(Fd.RegionList):
1400 if str(FdRegion.RegionType) is 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
1401 self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
1402 self.VPDSize = self.FdRegionList[index].Size
1403 break
1404
1405 if os.path.isfile(self.VpdFilePath):
1406 fd = open(self.VpdFilePath, "r")
1407 Lines = fd.readlines()
1408 for Line in Lines:
1409 Line = Line.strip()
1410 if len(Line) == 0 or Line.startswith("#"):
1411 continue
1412 try:
1413 PcdName, SkuId, Offset, Size, Value = Line.split("#")[0].split("|")
1414 PcdName, SkuId, Offset, Size, Value = PcdName.strip(), SkuId.strip(), Offset.strip(), Size.strip(), Value.strip()
1415 Offset = '0x%08X' % (int(Offset, 16) + self.VPDBaseAddress)
1416 self.VPDInfoList.append("%s | %s | %s | %s | %s" % (PcdName, SkuId, Offset, Size, Value))
1417 except:
1418 EdkLogger.error("BuildReport", CODE_ERROR, "Fail to parse VPD information file %s" % self.VpdFilePath)
1419 fd.close()
1420
1421 ##
1422 # Generate report for the firmware device.
1423 #
1424 # This function generates report for the firmware device.
1425 #
1426 # @param self The object pointer
1427 # @param File The file object for report
1428 #
1429 def GenerateReport(self, File):
1430 FileWrite(File, gSectionStart)
1431 FileWrite(File, "Firmware Device (FD)")
1432 FileWrite(File, "FD Name: %s" % self.FdName)
1433 FileWrite(File, "Base Address: %s" % self.BaseAddress)
1434 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1435 if len(self.FdRegionList) > 0:
1436 FileWrite(File, gSectionSep)
1437 for FdRegionItem in self.FdRegionList:
1438 FdRegionItem.GenerateReport(File)
1439
1440 if len(self.VPDInfoList) > 0:
1441 FileWrite(File, gSubSectionStart)
1442 FileWrite(File, "FD VPD Region")
1443 FileWrite(File, "Base Address: 0x%X" % self.VPDBaseAddress)
1444 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
1445 FileWrite(File, gSubSectionSep)
1446 for item in self.VPDInfoList:
1447 FileWrite(File, item)
1448 FileWrite(File, gSubSectionEnd)
1449 FileWrite(File, gSectionEnd)
1450
1451
1452
1453 ##
1454 # Reports platform information
1455 #
1456 # This class reports the whole platform information
1457 #
1458 class PlatformReport(object):
1459 ##
1460 # Constructor function for class PlatformReport
1461 #
1462 # This constructor function generates PlatformReport object a platform build.
1463 # It generates report for platform summary, flash, global PCDs and detailed
1464 # module information for modules involved in platform build.
1465 #
1466 # @param self The object pointer
1467 # @param Wa Workspace context information
1468 # @param MaList The list of modules in the platform build
1469 #
1470 def __init__(self, Wa, MaList, ReportType):
1471 self._WorkspaceDir = Wa.WorkspaceDir
1472 self.PlatformName = Wa.Name
1473 self.PlatformDscPath = Wa.Platform
1474 self.Architectures = " ".join(Wa.ArchList)
1475 self.ToolChain = Wa.ToolChain
1476 self.Target = Wa.BuildTarget
1477 self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1478 self.BuildEnvironment = platform.platform()
1479
1480 self.PcdReport = None
1481 if "PCD" in ReportType:
1482 self.PcdReport = PcdReport(Wa)
1483
1484 self.FdReportList = []
1485 if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1486 for Fd in Wa.FdfProfile.FdDict:
1487 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1488
1489 self.PredictionReport = None
1490 if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1491 self.PredictionReport = PredictionReport(Wa)
1492
1493 self.DepexParser = None
1494 if "DEPEX" in ReportType:
1495 self.DepexParser = DepexParser(Wa)
1496
1497 self.ModuleReportList = []
1498 if MaList != None:
1499 self._IsModuleBuild = True
1500 for Ma in MaList:
1501 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1502 else:
1503 self._IsModuleBuild = False
1504 for Pa in Wa.AutoGenObjectList:
1505 for ModuleKey in Pa.Platform.Modules:
1506 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, ReportType))
1507
1508
1509
1510 ##
1511 # Generate report for the whole platform.
1512 #
1513 # This function generates report for platform information.
1514 # It comprises of platform summary, global PCD, flash and
1515 # module list sections.
1516 #
1517 # @param self The object pointer
1518 # @param File The file object for report
1519 # @param BuildDuration The total time to build the modules
1520 # @param ReportType The kind of report items in the final report file
1521 #
1522 def GenerateReport(self, File, BuildDuration, ReportType):
1523 FileWrite(File, "Platform Summary")
1524 FileWrite(File, "Platform Name: %s" % self.PlatformName)
1525 FileWrite(File, "Platform DSC Path: %s" % self.PlatformDscPath)
1526 FileWrite(File, "Architectures: %s" % self.Architectures)
1527 FileWrite(File, "Tool Chain: %s" % self.ToolChain)
1528 FileWrite(File, "Target: %s" % self.Target)
1529 FileWrite(File, "Output Path: %s" % self.OutputPath)
1530 FileWrite(File, "Build Environment: %s" % self.BuildEnvironment)
1531 FileWrite(File, "Build Duration: %s" % BuildDuration)
1532 FileWrite(File, "Report Content: %s" % ", ".join(ReportType))
1533
1534 if not self._IsModuleBuild:
1535 if "PCD" in ReportType:
1536 self.PcdReport.GenerateReport(File, None)
1537
1538 if "FLASH" in ReportType:
1539 for FdReportListItem in self.FdReportList:
1540 FdReportListItem.GenerateReport(File)
1541
1542 for ModuleReportItem in self.ModuleReportList:
1543 ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
1544
1545 if not self._IsModuleBuild:
1546 if "EXECUTION_ORDER" in ReportType:
1547 self.PredictionReport.GenerateReport(File, None)
1548
1549 ## BuildReport class
1550 #
1551 # This base class contain the routines to collect data and then
1552 # applies certain format to the output report
1553 #
1554 class BuildReport(object):
1555 ##
1556 # Constructor function for class BuildReport
1557 #
1558 # This constructor function generates BuildReport object a platform build.
1559 # It generates report for platform summary, flash, global PCDs and detailed
1560 # module information for modules involved in platform build.
1561 #
1562 # @param self The object pointer
1563 # @param ReportFile The file name to save report file
1564 # @param ReportType The kind of report items in the final report file
1565 #
1566 def __init__(self, ReportFile, ReportType):
1567 self.ReportFile = ReportFile
1568 if ReportFile:
1569 self.ReportList = []
1570 self.ReportType = []
1571 if ReportType:
1572 for ReportTypeItem in ReportType:
1573 if ReportTypeItem not in self.ReportType:
1574 self.ReportType.append(ReportTypeItem)
1575 else:
1576 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1577 ##
1578 # Adds platform report to the list
1579 #
1580 # This function adds a platform report to the final report list.
1581 #
1582 # @param self The object pointer
1583 # @param Wa Workspace context information
1584 # @param MaList The list of modules in the platform build
1585 #
1586 def AddPlatformReport(self, Wa, MaList=None):
1587 if self.ReportFile:
1588 self.ReportList.append((Wa, MaList))
1589
1590 ##
1591 # Generates the final report.
1592 #
1593 # This function generates platform build report. It invokes GenerateReport()
1594 # method for every platform report in the list.
1595 #
1596 # @param self The object pointer
1597 # @param BuildDuration The total time to build the modules
1598 #
1599 def GenerateReport(self, BuildDuration):
1600 if self.ReportFile:
1601 try:
1602 File = StringIO('')
1603 for (Wa, MaList) in self.ReportList:
1604 PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType)
1605 Content = FileLinesSplit(File.getvalue(), gLineMaxLength)
1606 SaveFileOnChange(self.ReportFile, Content, True)
1607 EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
1608 except IOError:
1609 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1610 except:
1611 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1612 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1613 File.close()
1614
1615 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1616 if __name__ == '__main__':
1617 pass
1618