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