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