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