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