diff --git a/README.markdown b/README.markdown
index aee26ed..81381d5 100644
--- a/README.markdown
+++ b/README.markdown
@@ -4,7 +4,7 @@
Delphi Code Coverage is a simple Code Coverage tool for Delphi that creates code coverage reports
based on detailed MAP files.
-Please also check out [this project](http://code.google.com/p/delphi-code-coverage-wizard/) as it adds a wizard to the
+Please also check out [this project](https://github.com/trident-job/delphi-code-coverage-wizard) as it adds a wizard to the
Delphi IDE to help create configuration and launch Delphi Code Coverage.
## Preconditions
@@ -34,6 +34,11 @@ Open a command line prompt in the directory where your compiled application and
Type: `CodeCoverage -m TestApp.map -e TestApp.exe -u TestUnit TestUnit2 -xml -html`
+## Building
+
+Due to newer language features used, somewhat newer compiler is required. The project is known to not support Delphi XE2.
+XE3 will probably work. Main develop is done with 10.x versions.
+
## Output
### HTML output (specify `-html` as a parameter)
For each unit there will be a unit.html with a summary of the coverage, followed by the source marked up.
@@ -49,7 +54,7 @@ It is now possible to create EMMA compatible output which allows for using emma
well as using emma for generating reports.
### Delphi compatibility
-DCC is compatible with Delphi up tot 10.4.2, both 32 and 64 bit.
+DCC is compatible with Delphi up to 10.4.2, both 32 and 64 bit.
### SonarQube integration
You can integrate the results of the xml report in SonarQube. See the [Delphi SonarQube plugin](https://github.com/mendrix/SonarDelphi)
@@ -78,7 +83,7 @@ unfinished form on my harddrive for more than a year. Finally it slipped out.
-u TestUnit TestUnit2
The units that shall be checked for code coverage
-uf filename
Cover units listed in the file pointed to by filename. One unit per line in the file
-v
Show verbose output
-
-dproj ProjectFile.dproj
Parse the project file for source dirs
+
-dproj ProjectFile.dproj
Parse the project file for source dirs, executable name, code page and other options. Note that options that could only have single value, like code page, will be overwritten in the order of appearance if multiple related switches are encountered.
-a Param Param2
Parameters to pass on to the application that shall be checked for code coverage. ^ is an escape character
-lt [filename]
Log events to a text log file. Default file name is: Delphi-Code-Coverage-Debug.log
-lapi
Log events to the Windows API OutputDebugString
@@ -94,7 +99,9 @@ unfinished form on my harddrive for more than a year. Finally it slipped out.
-uns dll_or_exe unitname [unitname_2]
Create a separate namespace (the namespace name will be the name of the module without extension) ONLY for the listed units within the module
-mns name dll_or_exe [dll_or_exe_2]
Create a separate namespace with the given name for the listed dll:s. All modules loaded in those module(s) will be namespaced.
-lcl LineCountLimit
Count number of times a line is executed up to the specified limit
+
-cp CodePage
Code page number of source files
-tec
Passthrough the exitcode of the application inspected
+
-twd
Use the application's path as working directory
## License
diff --git a/SetupEnvironment.bat b/SetupEnvironment.bat
index a61d528..c3ad6ad 100644
--- a/SetupEnvironment.bat
+++ b/SetupEnvironment.bat
@@ -27,24 +27,6 @@ IF EXIST "%DPF%\Embarcadero\Studio\17.0\bin\rsvars.bat" (
IF EXIST "%DPF%\Embarcadero\Studio\14.0\bin\rsvars.bat" (
ECHO Found Delphi XE6
CALL "%DPF%\Embarcadero\Studio\14.0\bin\rsvars.bat"
-) ELSE (
- :: check for Delphi XE2
- IF EXIST "%DPF%\Embarcadero\RAD Studio\9.0\bin\rsvars.bat" (
- ECHO Found Delphi XE2
- CALL "%DPF%\Embarcadero\RAD Studio\9.0\bin\rsvars.bat"
- ) ELSE (
- :: Delphi 2010
- IF EXIST "%DPF%\Embarcadero\RAD Studio\7.0\bin\rsvars.bat" (
- ECHO Found Delphi 2010
- CALL "%DPF%\Embarcadero\RAD Studio\7.0\bin\rsvars.bat"
- ) ELSE (
- :: Delphi 2009
- IF EXIST "%DPF%\CodeGear\RAD Studio\6.0\bin\rsvars.bat" (
- ECHO Found Delphi 2009
- CALL "%DPF%\CodeGear\RAD Studio\6.0\bin\rsvars.bat"
- )
- )
- )
)
)
)
diff --git a/Source/BreakPoint.pas b/Source/BreakPoint.pas
index 7f2cad5..540e4b5 100644
--- a/Source/BreakPoint.pas
+++ b/Source/BreakPoint.pas
@@ -182,7 +182,8 @@ procedure TBreakPoint.Clear(const AThread: IDebugThread);
ContextRecord: CONTEXT;
Result: BOOL;
begin
- FLogManager.Log('Clearing BreakPoint at ' + IntToHex(Integer(FAddress), 8));
+ FLogManager.Log('Clearing BreakPoint at ' + IntToHex(NativeUInt(FAddress),
+ SizeOf(NativeUINT) * 2));
ContextRecord.ContextFlags := CONTEXT_CONTROL;
diff --git a/Source/CoverageConfiguration.pas b/Source/CoverageConfiguration.pas
index 92e05fa..91e3bf3 100644
--- a/Source/CoverageConfiguration.pas
+++ b/Source/CoverageConfiguration.pas
@@ -46,11 +46,13 @@ TCoverageConfiguration = class(TInterfacedObject, ICoverageConfiguration)
FXmlMergeGenerics: Boolean;
FHtmlOutput: Boolean;
FTestExeExitCode: Boolean;
+ FUseTestExePathAsWorkingDir: Boolean;
FExcludeSourceMaskLst: TStrings;
FLoadingFromDProj: Boolean;
FModuleNameSpaces: TModuleNameSpaceList;
FUnitNameSpaces: TUnitNameSpaceList;
FLineCountLimit: Integer;
+ FCodePage: Integer;
FLogManager: ILogManager;
procedure ReadSourcePathFile(const ASourceFileName: string);
@@ -58,7 +60,10 @@ TCoverageConfiguration = class(TInterfacedObject, ICoverageConfiguration)
procedure ParseSwitch(var AParameter: Integer);
procedure ParseBooleanSwitches;
function GetCurrentConfig(const Project: IXMLNode): string;
+ function GetBasePropertyGroupNode(const Project: IXMLNode): IXMLNode;
function GetExeOutputFromDProj(const Project: IXMLNode; const ProjectName: TFileName): string;
+ function GetSourceDirsFromDProj(const Project: IXMLNode): string;
+ function GetCodePageFromDProj(const Project: IXMLNode): Integer;
procedure ParseDProj(const DProjFilename: TFileName);
function IsPathInExclusionList(const APath: TFileName): Boolean;
procedure ExcludeSourcePaths;
@@ -87,6 +92,7 @@ TCoverageConfiguration = class(TInterfacedObject, ICoverageConfiguration)
procedure ParseModuleNameSpaceSwitch(var AParameter: Integer);
procedure ParseUnitNameSpaceSwitch(var AParameter: Integer);
procedure ParseLineCountSwitch(var AParameter: Integer);
+ procedure ParseCodePageSwitch(var AParameter: Integer);
public
constructor Create(const AParameterProvider: IParameterProvider);
destructor Destroy; override;
@@ -112,7 +118,9 @@ TCoverageConfiguration = class(TInterfacedObject, ICoverageConfiguration)
function XmlMergeGenerics: Boolean;
function HtmlOutput: Boolean;
function TestExeExitCode: Boolean;
+ function UseTestExePathAsWorkingDir: Boolean;
function LineCountLimit: Integer;
+ function CodePage: Integer;
function ModuleNameSpace(const AModuleName: string): TModuleNameSpace;
function UnitNameSpace(const AModuleName: string): TUnitNameSpace;
@@ -209,6 +217,11 @@ function TCoverageConfiguration.LineCountLimit: integer;
Result := FLineCountLimit;
end;
+function TCoverageConfiguration.CodePage: Integer;
+begin
+ Result := FCodePage;
+end;
+
function TCoverageConfiguration.IsComplete(var AReason: string): Boolean;
begin
if FSourcePathLst.Count = 0 then
@@ -311,7 +324,7 @@ procedure TCoverageConfiguration.OpenInputFileForReading(const AFileName: string
except
on E: EInOutError do
begin
- ConsoleOutput('Could not open:' + AFileName);
+ ConsoleOutput('Could not open: ' + AFileName);
raise ;
end;
end;
@@ -362,6 +375,11 @@ function TCoverageConfiguration.TestExeExitCode: Boolean;
Result := FTestExeExitCode;
end;
+function TCoverageConfiguration.UseTestExePathAsWorkingDir: Boolean;
+begin
+ Result := FUseTestExePathAsWorkingDir;
+end;
+
function TCoverageConfiguration.IsPathInExclusionList(const APath: TFileName): Boolean;
var
Mask: string;
@@ -396,6 +414,7 @@ procedure TCoverageConfiguration.ParseBooleanSwitches;
FHtmlOutput := IsSet(I_CoverageConfiguration.cPARAMETER_HTML_OUTPUT);
uConsoleOutput.G_Verbose_Output := IsSet(I_CoverageConfiguration.cPARAMETER_VERBOSE);
FTestExeExitCode := IsSet(I_CoverageConfiguration.cPARAMETER_TESTEXE_EXIT_CODE);
+ FUseTestExePathAsWorkingDir := IsSet(I_CoverageConfiguration.cPARAMETER_USE_TESTEXE_WORKING_DIR);
end;
procedure TCoverageConfiguration.ExcludeSourcePaths;
@@ -496,10 +515,10 @@ procedure TCoverageConfiguration.LogTracking;
CurrentUnit: string;
begin
for CurrentUnit in FUnitsStrLst do
- VerboseOutput('Will track coverage for:' + CurrentUnit);
+ VerboseOutput('Will track coverage for: ' + CurrentUnit);
for CurrentUnit in FExcludedUnitsStrLst do
- VerboseOutput('Exclude from coverage tracking for:' + CurrentUnit);
+ VerboseOutput('Exclude from coverage tracking for: ' + CurrentUnit);
end;
function TCoverageConfiguration.ParseParameter(const AParameter: Integer): string;
@@ -564,6 +583,8 @@ procedure TCoverageConfiguration.ParseSwitch(var AParameter: Integer);
FStripFileExtension := False
else if (SwitchItem = I_CoverageConfiguration.cPARAMETER_LINE_COUNT) then
ParseLineCountSwitch(AParameter)
+ else if (SwitchItem = I_CoverageConfiguration.cPARAMETER_CODE_PAGE) then
+ ParseCodePageSwitch(AParameter)
else if (SwitchItem = I_CoverageConfiguration.cPARAMETER_EMMA_OUTPUT)
or (SwitchItem = I_CoverageConfiguration.cPARAMETER_EMMA21_OUTPUT)
or (SwitchItem = I_CoverageConfiguration.cPARAMETER_EMMA_SEPARATE_META)
@@ -572,7 +593,8 @@ procedure TCoverageConfiguration.ParseSwitch(var AParameter: Integer);
or (SwitchItem = I_CoverageConfiguration.cPARAMETER_XML_LINES_MERGE_GENERICS)
or (SwitchItem = I_CoverageConfiguration.cPARAMETER_HTML_OUTPUT)
or (SwitchItem = I_CoverageConfiguration.cPARAMETER_VERBOSE)
- or (SwitchItem = I_CoverageConfiguration.cPARAMETER_TESTEXE_EXIT_CODE) then
+ or (SwitchItem = I_CoverageConfiguration.cPARAMETER_TESTEXE_EXIT_CODE)
+ or (SwitchItem = I_CoverageConfiguration.cPARAMETER_USE_TESTEXE_WORKING_DIR) then
begin
// do nothing, because its already parsed
end
@@ -887,13 +909,59 @@ function TCoverageConfiguration.GetCurrentConfig(const Project: IXMLNode): strin
end;
end;
+function TCoverageConfiguration.GetBasePropertyGroupNode(const Project: IXMLNode): IXMLNode;
+var
+ GroupIndex: Integer;
+begin
+ Assert(Assigned(Project));
+ for GroupIndex := 0 to Project.ChildNodes.Count - 1 do
+ begin
+ Result := Project.ChildNodes.Get(GroupIndex);
+ if (Result.LocalName = 'PropertyGroup')
+ and Result.HasAttribute('Condition')
+ and (
+ (Result.Attributes['Condition'] = '''$(Base)''!=''''')
+ or (Result.Attributes['Condition'] = '''$(Basis)''!=''''')
+ ) then
+ Exit;
+ end;
+ Result := nil;
+end;
+
+function TCoverageConfiguration.GetSourceDirsFromDProj(const Project: IXMLNode): string;
+var
+ Node: IXMLNode;
+begin
+ Result := '';
+ Assert(Assigned(Project));
+
+ Node := GetBasePropertyGroupNode(Project);
+ if Node = nil then Exit;
+ Node := Node.ChildNodes.FindNode('DCC_UnitSearchPath');
+ if Node = nil then Exit;
+ Result := StringReplace(Node.Text, '$(DCC_UnitSearchPath)', '', [rfReplaceAll, rfIgnoreCase]);
+end;
+
+function TCoverageConfiguration.GetCodePageFromDProj(const Project: IXMLNode): Integer;
+var
+ Node: IXMLNode;
+begin
+ Result := 0;
+ Assert(Assigned(Project));
+
+ Node := GetBasePropertyGroupNode(Project);
+ if Node = nil then Exit;
+ Node := Node.ChildNodes.FindNode('DCC_CodePage');
+ if Node = nil then Exit;
+ Result := StrToIntDef(Node.Text, 0);
+end;
+
function TCoverageConfiguration.GetExeOutputFromDProj(const Project: IXMLNode; const ProjectName: TFileName): string;
var
CurrentConfig: string;
CurrentPlatform: string;
DCC_ExeOutputNode: IXMLNode;
DCC_ExeOutput: string;
- GroupIndex: Integer;
Node: IXMLNode;
begin
Result := '';
@@ -906,15 +974,8 @@ function TCoverageConfiguration.GetExeOutputFromDProj(const Project: IXMLNode; c
CurrentPlatform := 'Win32';
{$ENDIF}
- for GroupIndex := 0 to Project.ChildNodes.Count - 1 do
- begin
- Node := Project.ChildNodes.Get(GroupIndex);
- if (Node.LocalName = 'PropertyGroup')
- and Node.HasAttribute('Condition')
- and (
- (Node.Attributes['Condition'] = '''$(Base)''!=''''')
- or (Node.Attributes['Condition'] = '''$(Basis)''!=''''')
- ) then
+ Node := GetBasePropertyGroupNode(Project);
+ if Node <> nil then
begin
if CurrentConfig <> '' then
begin
@@ -930,7 +991,6 @@ function TCoverageConfiguration.GetExeOutputFromDProj(const Project: IXMLNode; c
Result := ChangeFileExt(ProjectName, '.exe');
end;
end;
- end;
end;
procedure TCoverageConfiguration.ParseDProj(const DProjFilename: TFileName);
@@ -939,7 +999,7 @@ procedure TCoverageConfiguration.ParseDProj(const DProjFilename: TFileName);
ItemGroup: IXMLNode;
Node: IXMLNode;
Project: IXMLNode;
- Unitname: string;
+ Unitname, Path, SearchPaths: string;
I: Integer;
RootPath: TFileName;
SourcePath: TFileName;
@@ -960,6 +1020,20 @@ procedure TCoverageConfiguration.ParseDProj(const DProjFilename: TFileName);
FMapFileName := ChangeFileExt(FExeFileName, '.map');
end;
+ SearchPaths := GetSourceDirsFromDProj(Project);
+ if SearchPaths <> '' then
+ begin
+ for Path in SearchPaths.Split([';']) do
+ if Path <> '' then
+ begin
+ SourcePath := TPath.GetFullPath(TPath.Combine(RootPath, Path));
+ if FSourcePathLst.IndexOf(SourcePath) = -1 then
+ FSourcePathLst.Add(SourcePath);
+ end;
+ end;
+
+ FCodePage := GetCodePageFromDProj(Project);
+
ItemGroup := Project.ChildNodes.FindNode('ItemGroup');
if ItemGroup <> nil then
begin
@@ -1092,5 +1166,21 @@ procedure TCoverageConfiguration.ParseLineCountSwitch(var AParameter: Integer);
end;
end;
+procedure TCoverageConfiguration.ParseCodePageSwitch(var AParameter: Integer);
+var
+ ParsedParameter: string;
+begin
+ Inc(AParameter);
+ ParsedParameter := ParseParameter(AParameter);
+ if ParsedParameter.StartsWith('-') then // This is a switch, not a number
+ begin
+ Dec(AParameter);
+ end
+ else
+ begin
+ FCodePage := StrToIntDef(ParsedParameter, 0);
+ end;
+end;
+
end.
diff --git a/Source/DebugProcess.pas b/Source/DebugProcess.pas
index 0869ff0..e67375b 100644
--- a/Source/DebugProcess.pas
+++ b/Source/DebugProcess.pas
@@ -170,17 +170,20 @@ function TDebugProcess.MapScanner: TJCLMapScanner;
function TDebugProcess.FindDebugModuleFromAddress(Addr: Pointer): IDebugModule;
var
- CurrentModule: IDebugModule;
- ModuleAddress: DWORD;
+ ModuleAddress: NativeUINT;
function AddressBelongsToModule(const AModule: IDebugModule): Boolean;
begin
- Result := ((ModuleAddress >= AModule.Base)
- and (ModuleAddress <= (AModule.Base + AModule.Size)));
+ var Base := AModule.Base;
+ Result := ((ModuleAddress >= Base)
+ and (ModuleAddress <= (Base + AModule.Size)));
end;
+
+var
+ CurrentModule: IDebugModule;
begin
Result := nil;
- ModuleAddress := DWORD(Addr);
+ ModuleAddress := NativeUINT(Addr);
if AddressBelongsToModule(IDebugProcess(Self)) then
Result := IDebugProcess(self)
diff --git a/Source/Debugger.pas b/Source/Debugger.pas
index abdc212..0f2a127 100644
--- a/Source/Debugger.pas
+++ b/Source/Debugger.pas
@@ -13,6 +13,7 @@
interface
uses
+ Winapi.Windows,
System.Classes,
JclDebug,
JwaWinBase,
@@ -54,7 +55,8 @@ TDebugger = class(TInterfacedObject, IDebugger)
const AAddr: Pointer;
const AModule: HMODULE): DWORD; inline;
function GetImageName(const APtr: Pointer; const AUnicode: Word;
- const AlpBaseOfDll: Pointer; const AHandle: THANDLE): string;
+ const AlpBaseOfDll: Pointer; const AHandle: THANDLE;
+ const ADLLHandle: THandle): string;
procedure AddBreakPoints(
const AModuleList: TStrings;
const AExcludedModuleList: TStrings;
@@ -96,13 +98,6 @@ TDebugger = class(TInterfacedObject, IDebugger)
procedure Start;
end;
-function RealReadFromProcessMemory(
- const AhProcess: THANDLE;
- const AqwBaseAddress: DWORD64;
- const AlpBuffer: Pointer;
- const ASize: DWORD;
- var ANumberOfBytesRead: DWORD): BOOL; stdcall;
-
implementation
uses
@@ -131,25 +126,6 @@ implementation
DebugModule,
JclFileUtils, JclMapScannerHelper;
-function RealReadFromProcessMemory(
- const AhProcess: THANDLE;
- const AqwBaseAddress: DWORD64;
- const AlpBuffer: Pointer;
- const ASize: DWORD;
- var ANumberOfBytesRead: DWORD): BOOL; stdcall;
-var
- st: DWORD;
-begin
- Result := JwaWinBase.ReadProcessMemory(
- AhProcess,
- Pointer(AqwBaseAddress),
- AlpBuffer,
- ASize,
- @st
- );
- ANumberOfBytesRead := st;
-end;
-
constructor TDebugger.Create;
begin
inherited;
@@ -182,7 +158,7 @@ destructor TDebugger.Destroy;
procedure TDebugger.PrintUsage;
begin
- ConsoleOutput('Usage:CodeCoverage.exe [switches]');
+ ConsoleOutput('Usage: CodeCoverage.exe [switches]');
ConsoleOutput('List of switches:');
// --------------------------------------------------------------------------
ConsoleOutput('');
@@ -265,8 +241,12 @@ procedure TDebugger.PrintUsage;
' dll_or_exe unitname [unitname2] -- Create a separate namespace (the namespace name will be the name of the module without extension) *ONLY* for the listed units within the module.');
ConsoleOutput(I_CoverageConfiguration.cPARAMETER_LINE_COUNT +
' [number] -- Count number of times a line is executed up to the specified limit (default 0 - disabled)');
+ ConsoleOutput(I_CoverageConfiguration.cPARAMETER_CODE_PAGE +
+ ' [number] -- Code page of source files');
ConsoleOutput(I_CoverageConfiguration.cPARAMETER_TESTEXE_EXIT_CODE +
- ' [number] -- Passthrough the exitcode of the application');
+ ' -- Passthrough the exitcode of the application');
+ ConsoleOutput(I_CoverageConfiguration.cPARAMETER_USE_TESTEXE_WORKING_DIR +
+ ' -- Use the application''s path as working directory');
end;
@@ -307,7 +287,7 @@ procedure TDebugger.Start;
except
on E: EConfigurationException do
begin
- ConsoleOutput('Exception parsing the command line:' + E.message);
+ ConsoleOutput('Exception parsing the command line: ' + E.message);
PrintUsage;
end;
on E: Exception do
@@ -387,6 +367,7 @@ function TDebugger.StartProcessToDebug: Boolean;
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
Parameters: string;
+ WorkingDir: PChar;
begin
Parameters := FCoverageConfiguration.ApplicationParameters;
FLogManager.Log(
@@ -402,6 +383,12 @@ function TDebugger.StartProcessToDebug: Boolean;
StartInfo.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE);
StartInfo.hStdError := GetStdHandle(STD_ERROR_HANDLE);
+ WorkingDir := nil;
+ if FCoverageConfiguration.UseTestExePathAsWorkingDir then
+ begin
+ WorkingDir := PChar(ExtractFilePath(FCoverageConfiguration.ExeFileName));
+ end;
+
Parameters := '"' + FCoverageConfiguration.ExeFileName + '" ' + Parameters;
Result := CreateProcess(
nil,
@@ -411,7 +398,7 @@ function TDebugger.StartProcessToDebug: Boolean;
True,
CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS + DEBUG_PROCESS,
nil,
- nil,
+ WorkingDir,
StartInfo,
ProcInfo
);
@@ -467,7 +454,7 @@ procedure TDebugger.Debug;
ConsoleOutput(
'Unable to start executable "' +
FCoverageConfiguration.ExeFileName + '"');
- ConsoleOutput('Error :' + I_LogManager.LastErrorInfo);
+ ConsoleOutput('Error : ' + I_LogManager.LastErrorInfo);
end;
end
else
@@ -658,9 +645,17 @@ procedure TDebugger.AddBreakPoints(
' moduleName: ' + ModuleName +
' unitModuleName: ' + UnitModuleName +
' addr:' + IntToStr(LineIndex) +
+ {$IF CompilerVersion > 31}
' VA:' + IntToHex(MapLineNumber.VA) +
+ {$ELSE}
+ ' VA:' + IntToHex(MapLineNumber.VA, SizeOf(DWORD)*2) +
+ {$ENDIF}
' Base:' + IntToStr(AModule.Base) +
+ {$IF CompilerVersion > 31}
' Address: ' + IntToHex(Integer(AddressFromVA(MapLineNumber.VA, AModule.Base)))
+ {$ELSE}
+ ' Address: ' + IntToHex(Integer(AddressFromVA(MapLineNumber.VA, AModule.Base)), SizeOf(DWORD)*2)
+ {$ENDIF}
);
BreakPoint := FBreakPointList.BreakPointByAddress[(AddressFromVA(MapLineNumber.VA, AModule.Base))];
@@ -715,7 +710,8 @@ procedure TDebugger.AddBreakPoints(
end;
function TDebugger.GetImageName(const APtr: Pointer; const AUnicode: Word;
- const AlpBaseOfDll: Pointer; const AHandle: THANDLE): string;
+ const AlpBaseOfDll: Pointer; const AHandle: THANDLE;
+ const ADLLHandle: THandle): string;
var
PtrDllName: Pointer;
ByteRead: DWORD;
@@ -723,29 +719,37 @@ function TDebugger.GetImageName(const APtr: Pointer; const AUnicode: Word;
ImageName: array [0 .. MAX_PATH] of Char;
begin
Result := '';
- if (APtr <> nil) then
+ if GetFinalPathNameByHandle(ADLLHandle, ImageName, Length(ImageName), 0) > 0 then
+ begin
+ Result := string(ImageName);
+ end
+ else
begin
- if ReadProcessMemory(AHandle, APtr, @PtrDllName, sizeof(PtrDllName), @ByteRead) then
+ FLogManager.Log('Error ' + SysErrorMessage(GetLastError));
+ if APtr <> nil then
begin
- if (PtrDllName <> nil) then
+ if ReadProcessMemory(AHandle, APtr, @PtrDllName, sizeof(PtrDllName), @ByteRead) then
begin
- if ReadProcessMemory(AHandle, PtrDllName, @ImageName, sizeof(ImageName), @ByteRead) then
+ if PtrDllName <> nil then
begin
- if AUnicode <> 0 then
- Result := string(PWideChar(@ImageName))
- else
- Result := string(PChar(@ImageName));
+ if ReadProcessMemory(AHandle, PtrDllName, @ImageName, sizeof(ImageName), @ByteRead) then
+ begin
+ if AUnicode <> 0 then
+ Result := string(PWideChar(@ImageName))
+ else
+ Result := string(PChar(@ImageName));
+ end;
end;
- end;
- end
- else
- begin
- // if ReadProcessMemory failed
- FLogManager.Log('ReadProcessMemory error: ' + SysErrorMessage(GetLastError));
- if GetModuleFileNameEx (AHandle, HMODULE(AlpBaseOfDll), ImageName, MAX_PATH) = 0 then
- FLogManager.Log('GetModuleFileNameEx error: ' + SysErrorMessage(GetLastError))
+ end
else
- Result := string(PWideChar(@ImageName));
+ begin
+ // if ReadProcessMemory failed
+ FLogManager.Log('ReadProcessMemory error: ' + SysErrorMessage(GetLastError));
+ if GetModuleFileNameEx (AHandle, HMODULE(AlpBaseOfDll), ImageName, MAX_PATH) = 0 then
+ FLogManager.Log('GetModuleFileNameEx error: ' + SysErrorMessage(GetLastError))
+ else
+ Result := string(PWideChar(@ImageName));
+ end;
end;
end;
end;
@@ -762,7 +766,11 @@ procedure TDebugger.HandleCreateProcess(const ADebugEvent: DEBUG_EVENT);
PEImage := TJCLPEImage.Create;
try
PEImage.FileName := ProcessName;
+ {$IFDEF CPUX64}
+ Size := PEImage.OptionalHeader64.SizeOfCode;
+ {$ELSE}
Size := PEImage.OptionalHeader32.SizeOfCode;
+ {$ENDIF}
FProcessTarget := PEImage.Target;
finally
PEImage.Free;
@@ -847,7 +855,7 @@ procedure TDebugger.HandleExceptionDebug(
Cardinal(EXCEPTION_ACCESS_VIOLATION):
begin
FLogManager.Log(
- 'ACCESS VIOLATION at Address:' + IntToHex(Integer(ExceptionRecord.ExceptionAddress), 8));
+ 'ACCESS VIOLATION at Address:' + IntToHex(NativeUINT(ExceptionRecord.ExceptionAddress), SizeOf(NativeUINT) * 2));
FLogManager.Log(IntToHex(ExceptionRecord.ExceptionCode, 8) + ' not a debug BreakPoint');
if ExceptionRecord.NumberParameters > 1 then
@@ -860,7 +868,7 @@ procedure TDebugger.HandleExceptionDebug(
FLogManager.Log('DEP exception');
FLogManager.Log(
- 'Trying to access Address:' + IntToHex(Integer(ExceptionRecord.ExceptionInformation[1]), 8));
+ 'Trying to access Address:' + IntToHex(NativeUINT(ExceptionRecord.ExceptionInformation[1]),SizeOf(NativeUINT) * 2));
if Assigned(MapScanner) then
begin
@@ -881,12 +889,12 @@ procedure TDebugger.HandleExceptionDebug(
if not Assigned(Module) then
FLogManager.Log(
'No map information available Address:' +
- IntToHex(Integer(ExceptionRecord.ExceptionInformation[1]), 8) +
+ IntToHex(NativeUINT(ExceptionRecord.ExceptionInformation[1]), SizeOf(NativeUINT) * 2) +
' in unknown module')
else
FLogManager.Log(
'No map information available Address:' +
- IntToHex(Integer(ExceptionRecord.ExceptionInformation[1]), 8) +
+ IntToHex(NativeUINT(ExceptionRecord.ExceptionInformation[1]), SizeOf(NativeUINT) * 2) +
' module ' + Module.Name);
end;
@@ -957,7 +965,7 @@ procedure TDebugger.HandleExceptionDebug(
// A good contender for this is ntdll.DbgBreakPoint {$7C90120E}
FLogManager.Log(
'Couldn''t find BreakPoint for exception address:' +
- IntToHex(Integer(ExceptionRecord.ExceptionAddress), 8));
+ IntToHex(NativeUINT(ExceptionRecord.ExceptionAddress), SizeOf(NativeUINT) * 2));
end;
ADebugEventHandlingResult := Cardinal(DBG_CONTINUE);
end;
@@ -977,7 +985,7 @@ procedure TDebugger.HandleExceptionDebug(
begin
FLogManager.Log(
'EXCEPTION_DATATYPE_MISALIGNMENT Address:' +
- IntToHex(Integer(ExceptionRecord.ExceptionAddress), 8));
+ IntToHex(NativeUINT(ExceptionRecord.ExceptionAddress), SizeOf(NativeUINT) * 2));
FLogManager.Log(
IntToHex(ExceptionRecord.ExceptionCode, 8) + ' not a debug BreakPoint');
AContProcessEvents := False;
@@ -1002,7 +1010,8 @@ procedure TDebugger.HandleExceptionDebug(
else
begin
FLogManager.Log('EXCEPTION CODE:' + IntToHex(ExceptionRecord.ExceptionCode, 8));
- FLogManager.Log('Address:' + IntToHex(Integer(ExceptionRecord.ExceptionAddress), 8));
+ FLogManager.Log('Address:' + IntToHex(NativeUINT(ExceptionRecord.ExceptionAddress),
+ SizeOf(NativeUINT) * 2));
FLogManager.Log('EXCEPTION flags:' + IntToHex(ExceptionRecord.ExceptionFlags, 8));
LogStackFrame(ADebugEvent);
end;
@@ -1018,7 +1027,7 @@ procedure TDebugger.LogStackFrame(const ADebugEvent: DEBUG_EVENT);
DebugThread: IDebugThread;
Module: IDebugModule;
MapScanner: TJCLMapScanner;
- MachineType: Cardinal;
+ MachineType: DWORD;
begin
ContextRecord.ContextFlags := CONTEXT_ALL;
case FProcessTarget of
@@ -1052,15 +1061,6 @@ procedure TDebugger.LogStackFrame(const ADebugEvent: DEBUG_EVENT);
StackFrame.AddrFrame.Mode := AddrModeFlat;
StackFrame.AddrStack.Mode := AddrModeFlat;
- StackWalk64(
- MachineType,
- FDebugProcess.Handle,
- DebugThread.Handle,
- StackFrame,
- @ContextRecord,
- @RealReadFromProcessMemory,
- nil, nil, nil);
-
FLogManager.Log('---------------Stack trace --------------');
while StackWalk64(
MachineType,
@@ -1068,20 +1068,19 @@ procedure TDebugger.LogStackFrame(const ADebugEvent: DEBUG_EVENT);
DebugThread.Handle,
StackFrame,
@ContextRecord,
- @RealReadFromProcessMemory,
- nil, nil, nil
+ nil, nil, nil, nil
) do
begin
- if (StackFrame.AddrPC.Offset <> 0) then
+ if StackFrame.AddrPC.Offset <> 0 then
begin
Module := FDebugProcess.FindDebugModuleFromAddress(Pointer(StackFrame.AddrPC.Offset));
- if (Module <> nil) then
+ if Module <> nil then
begin
MapScanner := Module.MapScanner;
FLogManager.Log(
'Module : ' + Module.Name +
- ' Stack frame:' + IntToHex(Cardinal(Pointer(StackFrame.AddrPC.Offset)), 8));
+ ' Stack frame:' + IntToHex(NativeUINT(Pointer(StackFrame.AddrPC.Offset)), SizeOf(NativeUINT) * 2));
if Assigned(MapScanner) then
begin
for LineIndex := 0 to MapScanner.LineNumbersCnt - 1 do
@@ -1115,7 +1114,7 @@ procedure TDebugger.LogStackFrame(const ADebugEvent: DEBUG_EVENT);
begin
FLogManager.Log(
'No module found for exception address:' +
- IntToHex(StackFrame.AddrPC.Offset, 8));
+ IntToHex(StackFrame.AddrPC.Offset, SizeOf(DWORD64) * 2));
end;
end;
end;
@@ -1162,7 +1161,7 @@ procedure TDebugger.HandleLoadDLL(const ADebugEvent: DEBUG_EVENT);
ADebugEvent.LoadDll.lpImageName,
ADebugEvent.LoadDll.fUnicode,
ADebugEvent.LoadDll.lpBaseOfDll,
- FDebugProcess.Handle);
+ FDebugProcess.Handle, ADebugEvent.LoadDll.hFile);
if DllName = 'WOW64_IMAGE_SECTION' then
begin
@@ -1174,7 +1173,11 @@ procedure TDebugger.HandleLoadDLL(const ADebugEvent: DEBUG_EVENT);
PEImage := TJCLPEImage.Create;
try
PEImage.FileName := DllName;
+ {$IFDEF CPUX64}
+ Size := PEImage.OptionalHeader64.SizeOfCode;
+ {$ELSE}
Size := PEImage.OptionalHeader32.SizeOfCode;
+ {$ENDIF}
finally
PEImage.Free;
end;
@@ -1200,7 +1203,7 @@ procedure TDebugger.HandleLoadDLL(const ADebugEvent: DEBUG_EVENT);
ExtraMsg := ' (' + DllName + ') size :' + IntToStr(Size);
FLogManager.Log(
- 'Loading DLL at addr:' + IntToHex(DWORD(ADebugEvent.LoadDll.lpBaseOfDll), 8) +
+ 'Loading DLL at addr:' + IntToHex(NativeUINT(ADebugEvent.LoadDll.lpBaseOfDll), SizeOf(NativeUINT)*2) +
ExtraMsg);
ModuleNameSpace := FCoverageConfiguration.ModuleNameSpace(ExtractFileName(DllName));
@@ -1232,7 +1235,7 @@ procedure TDebugger.HandleLoadDLL(const ADebugEvent: DEBUG_EVENT);
procedure TDebugger.HandleUnLoadDLL(const ADebugEvent: DEBUG_EVENT);
begin
FLogManager.Log(
- 'UnLoading DLL:' + IntToHex(DWORD(ADebugEvent.LoadDll.lpBaseOfDll), 8));
+ 'UnLoading DLL:' + IntToHex(NativeUINT(ADebugEvent.LoadDll.lpBaseOfDll), SizeOf(NativeUINT) * 2));
end;
procedure TDebugger.HandleOutputDebugString(const ADebugEvent: DEBUG_EVENT);
diff --git a/Source/HTMLCoverageReport.pas b/Source/HTMLCoverageReport.pas
index 9f97095..885fc14 100644
--- a/Source/HTMLCoverageReport.pas
+++ b/Source/HTMLCoverageReport.pas
@@ -69,6 +69,9 @@ THTMLCoverageReport = class(TInterfacedObject, IReport)
function GenerateUnitReport(const ACoverageUnit: ICoverageStats): THtmlDetails;
procedure AddGeneratedAt(var OutputFile: TTextWriter);
+
+ function PrettyPercentage(nbItems, nbTotal : Integer) : String;
+
public
constructor Create(const ACoverageConfiguration: ICoverageConfiguration);
@@ -79,18 +82,16 @@ THTMLCoverageReport = class(TInterfacedObject, IReport)
end;
const
- SourceClass: string = ' class="s"';
- OverviewClass: string = ' class="o"';
- SummaryClass: string = ' class="sum"';
+ SourceClass: string = 's';
+ OverviewClass: string = 'o';
+ SummaryClass: string = 'sum';
implementation
uses
System.SysUtils,
- System.Math,
System.NetEncoding,
- JclFileUtils,
- HtmlHelper;
+ JclFileUtils;
procedure THTMLCoverageReport.Generate(
const ACoverage: ICoverageStats;
@@ -113,7 +114,7 @@ procedure THTMLCoverageReport.Generate(
OutputFile := TStreamWriter.Create(OutputFileName, False, TEncoding.UTF8);
try
AddPreAmble(OutputFile);
- OutputFile.WriteLine(heading('Summary Coverage Report', 1));
+ OutputFile.WriteLine('