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