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