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