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