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