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