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