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