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