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