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