diff --git a/DbgHelpUtils/CMakeLists.txt b/DbgHelpUtils/CMakeLists.txt
index 191db55..d0397b7 100644
--- a/DbgHelpUtils/CMakeLists.txt
+++ b/DbgHelpUtils/CMakeLists.txt
@@ -333,6 +333,7 @@ set(Utility
"hex_dump.h"
"join.h"
"memory_range.h"
+ "null_stream.h"
"overload.h"
"locale_number_formatting.cpp"
"locale_number_formatting.h"
diff --git a/DbgHelpUtils/DbgHelpUtils.vcxproj b/DbgHelpUtils/DbgHelpUtils.vcxproj
index 6424244..472cb69 100644
--- a/DbgHelpUtils/DbgHelpUtils.vcxproj
+++ b/DbgHelpUtils/DbgHelpUtils.vcxproj
@@ -342,6 +342,7 @@
+
diff --git a/DbgHelpUtils/DbgHelpUtils.vcxproj.filters b/DbgHelpUtils/DbgHelpUtils.vcxproj.filters
index eb507e4..c686633 100644
--- a/DbgHelpUtils/DbgHelpUtils.vcxproj.filters
+++ b/DbgHelpUtils/DbgHelpUtils.vcxproj.filters
@@ -959,6 +959,9 @@
Heap
+
+ Utility
+
diff --git a/DbgHelpUtils/filesystem_utils.h b/DbgHelpUtils/filesystem_utils.h
index 544e1e5..7856612 100644
--- a/DbgHelpUtils/filesystem_utils.h
+++ b/DbgHelpUtils/filesystem_utils.h
@@ -1,5 +1,6 @@
#pragma once
#include
+#include
#include
#include
diff --git a/DbgHelpUtils/null_stream.h b/DbgHelpUtils/null_stream.h
new file mode 100644
index 0000000..f3c9743
--- /dev/null
+++ b/DbgHelpUtils/null_stream.h
@@ -0,0 +1,27 @@
+#pragma once
+#include
+
+class null_stream : public std::wostream
+{
+private:
+ class null_buffer : public std::wstreambuf
+ {
+ public:
+ int_type overflow( int_type const ch ) override
+ {
+ return ch;
+ }
+
+ // ReSharper disable once IdentifierTypo
+ std::streamsize xsputn( [[maybe_unused]] const char_type* s, std::streamsize const count ) override
+ {
+ return count;
+ }
+ } nb_;
+
+public:
+ null_stream()
+ : std::wostream{&nb_}
+ {
+ }
+};
diff --git a/MiniDumper/dump_mini_dump_heap_statistics.cpp b/MiniDumper/dump_mini_dump_heap_statistics.cpp
index d2bc934..560c559 100644
--- a/MiniDumper/dump_mini_dump_heap_statistics.cpp
+++ b/MiniDumper/dump_mini_dump_heap_statistics.cpp
@@ -181,15 +181,21 @@ void dump_mini_dump_heap_statistics(std::wostream& log, mini_dump const& mini_du
auto const loading_heap_statistics = L"Loading heap statistics..."s;
std::wstring const move_back(loading_heap_statistics.size(), L'\b');
std::wstring const clear(loading_heap_statistics.size(), L' ');
- std::wcerr << loading_heap_statistics;
+ if(options.verbose_output())
+ {
+ std::wcerr << loading_heap_statistics;
+ }
auto const hex_length = heaps.peb().machine_hex_printable_length();
auto const is_x86_target = stream_stack_dump::is_x86_target_t{heaps.peb().is_x86_target()};
auto const statistics = heaps.statistics();
- std::wcerr << move_back;
- std::wcerr << clear;
- std::wcerr << move_back;
+ if(options.verbose_output())
+ {
+ std::wcerr << move_back;
+ std::wcerr << clear;
+ std::wcerr << move_back;
+ }
log << L"Heap Statistics:\n";
if(options.display_heap_statistic_view(heap_statistics_view::by_size_frequency_view))
diff --git a/MiniDumper/symbol_engine_ui.cpp b/MiniDumper/symbol_engine_ui.cpp
index 05861f5..a78b427 100644
--- a/MiniDumper/symbol_engine_ui.cpp
+++ b/MiniDumper/symbol_engine_ui.cpp
@@ -24,24 +24,30 @@ void symbol_engine_ui::deferred_symbol_load_partial([[maybe_unused]] std::wstrin
void symbol_engine_ui::start_download(std::wstring_view const& module_name)
{
module_ = std::format(L"downloading {}: ", module_name);
- std::wcerr << module_;
- last_percent_.clear();
+ if(options_.verbose_output())
+ {
+ std::wcerr << module_;
+ last_percent_.clear();
+ }
}
void symbol_engine_ui::download_percent(unsigned const percent)
{
- if (!last_percent_.empty())
+ if (options_.verbose_output() && !last_percent_.empty())
{
std::wcerr << std::wstring(last_percent_.size(), L'\b');
}
last_percent_ = std::format(L"{}%", percent);
- std::wcerr << last_percent_;
+ if(options_.verbose_output())
+ {
+ std::wcerr << last_percent_;
+ }
}
void symbol_engine_ui::download_complete()
{
- if (!last_percent_.empty() || !module_.empty())
+ if (options_.verbose_output() && (!last_percent_.empty() || !module_.empty()))
{
std::wstring const clear(last_percent_.size() + module_.size(), L'\b');
std::wcerr << clear << std::wstring(last_percent_.size() + module_.size(), L' ') << clear;
@@ -52,7 +58,12 @@ void symbol_engine_ui::download_complete()
std::wostream& symbol_engine_ui::log_stream() const
{
- return std::wcerr;
+ if(options_.verbose_output())
+ {
+ return std::wcerr;
+ }
+
+ return null_stream_;
}
bool symbol_engine_ui::symbol_load_debug() const
diff --git a/MiniDumper/symbol_engine_ui.h b/MiniDumper/symbol_engine_ui.h
index 051ea8f..109a9ab 100644
--- a/MiniDumper/symbol_engine_ui.h
+++ b/MiniDumper/symbol_engine_ui.h
@@ -2,6 +2,7 @@
#include "dump_file_options.h"
#include "DbgHelpUtils/i_symbol_load_callback.h"
+#include "DbgHelpUtils/null_stream.h"
class symbol_engine_ui : public dlg_help_utils::dbg_help::i_symbol_load_callback
{
@@ -21,4 +22,5 @@ class symbol_engine_ui : public dlg_help_utils::dbg_help::i_symbol_load_callback
dump_file_options const& options_;
std::wstring module_;
std::wstring last_percent_;
+ mutable null_stream null_stream_;
};
diff --git a/RunAllBuildHeapDumpTests.ps1 b/RunAllBuildHeapDumpTests.ps1
index 83fd2b6..ec507ea 100644
--- a/RunAllBuildHeapDumpTests.ps1
+++ b/RunAllBuildHeapDumpTests.ps1
@@ -5,6 +5,7 @@ Param
[switch] $CheckOnly,
[switch] $GenerateHeapLogs,
[switch] $LastReport,
+ [int] $ConcurrentLimit = 0,
[switch] $Verbose,
[switch] $ExitOnFailure
)
@@ -40,19 +41,23 @@ if(!$LastReport)
# run release / x64
Write-Host "Running $Compiler Release x64 tests"
- .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$false -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure
+ Write-Verbose "Command: \RunHeapDumpTests.ps1 -TestX86:`$false -TestDebug:`$false -CheckOnly:`$$CheckOnly -GenerateHeapLogs:`$$GenerateHeapLogs -Verbose:`$$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:`$$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit"
+ .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$false -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit
# run release / x86
Write-Host "Running $Compiler Release x86 tests"
- .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$false -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure
+ Write-Verbose "Command: \RunHeapDumpTests.ps1 -TestX86:`$true -TestDebug:`$false -CheckOnly:`$$CheckOnly -GenerateHeapLogs:`$$GenerateHeapLogs -Verbose:`$$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:`$$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit"
+ .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$false -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit
# run debug / x64
Write-Host "Running $Compiler Debug x64 tests"
- .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$true -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure
+ Write-Verbose "Command: \RunHeapDumpTests.ps1 -TestX86:`$false -TestDebug:`$true -CheckOnly:`$$CheckOnly -GenerateHeapLogs:`$$GenerateHeapLogs -Verbose:`$$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:`$$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit"
+ .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$true -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit
# run debug / x86
Write-Host "Running $Compiler Debug x86 tests"
- .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$true -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure
+ Write-Verbose "Command: \RunHeapDumpTests.ps1 -TestX86:`$true -TestDebug:`$true -CheckOnly:`$$CheckOnly -GenerateHeapLogs:`$$GenerateHeapLogs -Verbose:`$$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:`$$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit"
+ .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$true -CheckOnly:$CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -ExitOnFailure:$ExitOnFailure -ConcurrentLimit:$ConcurrentLimit
}
@@ -74,6 +79,7 @@ if($errorLines)
{
Write-Host "Complete with Errors:"
Write-Host $errorLines
+ Write-Host "Completed with Errors"
}
else
{
diff --git a/RunAllFailedTests.ps1 b/RunAllFailedTests.ps1
index 8ebc74b..e9c0a68 100644
--- a/RunAllFailedTests.ps1
+++ b/RunAllFailedTests.ps1
@@ -5,13 +5,92 @@ Param
[switch] $GenerateHeapLogs,
[switch] $LastReport,
[string[]] $Filter,
+ [int] $ConcurrentLimit = 0,
[switch] $Verbose
)
+if($Verbose)
+{
+ $VerbosePreference = "continue"
+}
+
+if ($ConcurrentLimit -le 0)
+{
+ $ConcurrentLimit = $env:NUMBER_OF_PROCESSORS
+}
+
+$engine = Get-Process -id $pid | Get-Item
+
+Function GenerateTempReportFile($tempReportFiles)
+{
+ $tempFile = New-TemporaryFile
+ $tempReportFiles.Add($tempFile)
+ return $tempFile
+}
+
+Function RunTest($jobs, $tempReportFiles, $testArguments)
+{
+ if($ConcurrentLimit -eq 1)
+ {
+ $tempFile = GenerateTempReportFile $tempReportFiles
+ Write-Verbose "Command: .\RunHeapDumpTests.ps1 $testArguments -ResultFile:`"$($tempFile.FullName)`" -ConcurrentLimit:1"
+ Invoke-Expression -Command ".\RunHeapDumpTests.ps1 $testArguments -ResultFile:`"$($tempFile.FullName)`" -ConcurrentLimit:1"
+ return
+ }
+
+ # waiting for a job to complete to start another job
+ if(($jobs | Where-Object -FilterScript { -not $_.HasExited }).Count -ge $ConcurrentLimit)
+ {
+ Write-Verbose "Waiting to start job"
+ while(($jobs | Where-Object -FilterScript { -not $_.HasExited }).Count -ge $ConcurrentLimit)
+ {
+ Start-Sleep -Milliseconds 500
+ }
+ Write-Verbose "Wait complete"
+ }
+
+ $tempFile = GenerateTempReportFile $tempReportFiles
+ Write-Verbose "Command: $($engine.FullName) -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\RunHeapDumpTests.ps1 $testArguments -ResultFile:`"$($tempFile.FullName)`""
+ $arguments = "-NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\RunHeapDumpTests.ps1 $testArguments -ResultFile:`"$($tempFile.FullName)`""
+
+ $pinfo = New-Object System.Diagnostics.ProcessStartInfo
+ $pinfo.FileName = $engine.FullName
+ $pinfo.UseShellExecute = $false
+ $pinfo.Arguments = $arguments
+ $pinfo.WorkingDirectory = Get-Location
+ $p = New-Object System.Diagnostics.Process
+ $p.StartInfo = $pinfo
+ $p.Start() | Out-Null
+
+ $jobs.Add($p)
+ Write-Verbose "Started job [$($p.Id)]"
+}
+
+Function RunTests($jobs, $tempReportFiles, $type, $name, $baseArguments)
+{
+ # run release / x64
+ Write-Host "Running $Compiler Release x64 test on $type $name"
+ $rv = RunTest $jobs $tempReportFiles "-TestX86:`$false -TestDebug:`$false $baseArguments"
+
+ # run release / x86
+ Write-Host "Running $Compiler Release x86 tests on $type $name"
+ $rv = RunTest $jobs $tempReportFiles "-TestX86:`$true -TestDebug:`$false $baseArguments"
+
+ # run debug / x64
+ Write-Host "Running $Compiler Debug x64 tests on $type $name"
+ $rv = RunTest $jobs $tempReportFiles "-TestX86:`$false -TestDebug:`$true $baseArguments"
+
+ # run debug / x86
+ Write-Host "Running $Compiler Debug x86 tests on $type $name"
+ $rv = RunTest $jobs $tempReportFiles "-TestX86:`$true -TestDebug:`$true $baseArguments"
+}
+
if(!$LastReport)
{
Remove-Item $ResultFile -ErrorAction:SilentlyContinue | Out-Null
$dmps = Get-ChildItem failed\*.dmp -Recurse
+ $tempReportFiles = New-Object System.Collections.ArrayList
+ $jobs = New-Object System.Collections.ArrayList
foreach ($dmp in $dmps)
{
$CheckDumpHasStackTrace = $dmp.BaseName -match '_ust'
@@ -29,43 +108,45 @@ if(!$LastReport)
elseif ($dmp.BaseName -match '_1$')
{
$name = $dmp.BaseName.SubString(0, $dmp.BaseName.Length - 2)
- # run release / x64
- Write-Host "Running $Compiler Release x64 test on set $name"
- .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$false -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $name -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -DumpFolder $dmp.Directory
-
- # run release / x86
- Write-Host "Running $Compiler Release x86 tests on set $name"
- .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$false -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $name -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -DumpFolder $dmp.Directory
+ $baseArguments = "-CheckOnly -GenerateHeapLogs:`$$GenerateHeapLogs -Verbose:`$$Verbose -Compiler:$Compiler -CheckDumpFileBaseName `"$name`" -CheckDumpHasStackTrace:`$$CheckDumpHasStackTrace -SkipFakeOffsetCheck:`$$SkipFakeOffsetCheck -DumpFolder `"$($dmp.Directory)`" -ConcurrentLimit:$ConcurrentLimit"
- # run debug / x64
- Write-Host "Running $Compiler Debug x64 tests on set $name"
- .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$true -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $name -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -DumpFolder $dmp.Directory
-
- # run debug / x86
- Write-Host "Running $Compiler Debug x86 tests on set $name"
- .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$true -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $name -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -DumpFolder $dmp.Directory
+ RunTests $jobs $tempReportFiles 'set' $name $baseArguments
}
else
{
- # run release / x64
- Write-Host "Running $Compiler Release x64 test on single $dmp"
- .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$false -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $dmp.BaseName -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -SingleDumpOnly -DumpFolder $dmp.Directory
+ $baseArguments = "-CheckOnly -GenerateHeapLogs:`$$GenerateHeapLogs -Verbose:`$$Verbose -Compiler:$Compiler -CheckDumpFileBaseName `"$($dmp.BaseName)`" -CheckDumpHasStackTrace:`$$CheckDumpHasStackTrace -SkipFakeOffsetCheck:`$$SkipFakeOffsetCheck -SingleDumpOnly -DumpFolder `"$($dmp.Directory)`" -ConcurrentLimit:$ConcurrentLimit"
+ RunTests $jobs $tempReportFiles 'single' $dmp $baseArguments
+ }
+ }
- # run release / x86
- Write-Host "Running $Compiler Release x86 tests on single $dmp"
- .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$false -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $dmp.BaseName -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -SingleDumpOnly -DumpFolder $dmp.Directory
+ Write-Verbose "Waiting for all jobs to complete"
+ # wait for all jobs
+ foreach($p in $jobs)
+ {
+ $p.WaitForExit()
+ }
- # run debug / x64
- Write-Host "Running $Compiler Debug x64 tests on single $dmp"
- .\RunHeapDumpTests.ps1 -TestX86:$false -TestDebug:$true -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $dmp.BaseName -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -SingleDumpOnly -DumpFolder $dmp.Directory
+ Write-Verbose "Combining report output"
- # run debug / x86
- Write-Host "Running $Compiler Debug x86 tests on single $dmp"
- .\RunHeapDumpTests.ps1 -TestX86:$true -TestDebug:$true -CheckOnly -GenerateHeapLogs:$GenerateHeapLogs -Verbose:$Verbose -ResultFile:$ResultFile -Compiler:$Compiler -CheckDumpFileBaseName $dmp.BaseName -CheckDumpHasStackTrace:$CheckDumpHasStackTrace -SkipFakeOffsetCheck:$SkipFakeOffsetCheck -SingleDumpOnly -DumpFolder $dmp.Directory
+ # Combine result files
+ foreach ($reportFile in $tempReportFiles)
+ {
+ if(Test-Path -Path $reportFile -PathType Leaf)
+ {
+ Write-Verbose "Append $reportFile to $ResultFile"
+ Get-Content $reportFile | Add-Content $ResultFile
+ } else {
+ Write-Error "Report File $reportFile doesn't exist!"
}
}
+
+ # wait for the above to complete it's file handling as the file may still be locked when we get to the remove temp files stage...
+ #Start-Sleep -Seconds 5
+
+ #$tempReportFiles | Remove-Item
}
+Write-Verbose "Reading report output"
$errorLines = ""
$log = Get-Content $ResultFile
@@ -85,6 +166,7 @@ if($errorLines)
{
Write-Host "Complete with Errors:"
Write-Host $errorLines
+ Write-Host "Completed with Errors"
}
else
{
diff --git a/RunHeapDumpTests.ps1 b/RunHeapDumpTests.ps1
index b4c2a33..e9d62ac 100644
--- a/RunHeapDumpTests.ps1
+++ b/RunHeapDumpTests.ps1
@@ -25,14 +25,57 @@ Param
[switch] $CheckDumpHasStackTrace,
[switch] $SkipFakeOffsetCheck,
[switch] $SingleDumpOnly,
+ [int] $ConcurrentLimit = 0,
[switch] $ExitOnFailure
)
-Function RunCommand($command, $arguments)
+$runningProcesses = New-Object System.Collections.ArrayList
+$errorsDetected = $false
+
+if ($ConcurrentLimit -le 0)
{
- $text = "$command $arguments"
- Write-Verbose "Run: $text"
+ $ConcurrentLimit = $env:NUMBER_OF_PROCESSORS
+}
+
+Function DetectCompletedJobWithErrors()
+{
+ foreach($job in $runningProcesses | Where-Object -FilterScript { $_.Process.HasExited -and $_.Process.ExitCode -ne 0 })
+ {
+ return $true
+ }
+
+ return $false
+}
+
+Function WaitForAllProcessesToComplete()
+{
+ $err = $null
+ foreach($p in $runningProcesses)
+ {
+ Write-Verbose "Waiting for $($p.Process.ProcessName) PID $($p.Process.Id)"
+ $p.Process.WaitForExit()
+ $exitCode = $p.Process.ExitCode
+
+ if($p.ReportFile)
+ {
+ Write-Verbose "Add $($p.ReportFile) to $ResultFile"
+ Get-Content -Path $p.ReportFile | Add-Content -Path $ResultFile
+ Remove-Item $p.ReportFile
+ }
+
+ if($exitCode -ne 0)
+ {
+ Write-Verbose "Add $($p.Process.ProcessName) Command Error to $ResultFile"
+ $text = "$($p.Process.StartInfo.FileName) $($p.Process.StartInfo.Arguments)"
+ $err = "ERROR: command failed: [$text] -- with $exitCode"
+ Add-Content -Path $ResultFile $err
+ Write-Error $err
+ }
+ }
+}
+Function RunCommandNow($command, $arguments)
+{
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $command
$pinfo.UseShellExecute = $false
@@ -42,22 +85,71 @@ Function RunCommand($command, $arguments)
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
- $exitCode = $p.ExitCode
-
- if($exitCode -ne 0)
+}
+
+Function RunCommand($command, $arguments, $reportFile)
+{
+ if($errorsDetected)
+ {
+ return;
+ }
+
+ if($ConcurrentLimit -eq 1)
{
- $err = "ERROR: command failed: [$text] -- with $exitCode"
- Add-Content -Path $ResultFile $err
+ $pinfo = New-Object System.Diagnostics.ProcessStartInfo
+ $pinfo.FileName = $command
+ $pinfo.UseShellExecute = $false
+ $pinfo.Arguments = $arguments
+ $pinfo.WorkingDirectory = Get-Location
+ $p = New-Object System.Diagnostics.Process
+ $p.StartInfo = $pinfo
+ $p.Start() | Out-Null
+ $p.WaitForExit()
+ $data = New-Object PSObject -Property @{
+ Process = $p
+ ReportFile = $reportFile
+ }
+ $rv = $runningProcesses.Add($data)
+ return
+ }
- if($ExitOnFailure)
+ # waiting for a job to complete to start another job
+ if(($runningProcesses | Where-Object -FilterScript { -not $_.Process.HasExited }).Count -ge $ConcurrentLimit)
+ {
+ Write-Verbose "Waiting to start process"
+ while(($runningProcesses | Where-Object -FilterScript { -not $_.Process.HasExited }).Count -ge $ConcurrentLimit)
{
- throw $err
+ Start-Sleep -Milliseconds 500
}
- else
+ Write-Verbose "Wait complete"
+ }
+
+ if($ExitOnFailure)
+ {
+ $errorsDetected = DetectCompletedJobWithErrors
+
+ if($errorsDetected)
{
- Write-Error $err
+ return;
}
}
+
+ $text = "$command $arguments"
+ Write-Verbose "Run: $text"
+
+ $pinfo = New-Object System.Diagnostics.ProcessStartInfo
+ $pinfo.FileName = $command
+ $pinfo.UseShellExecute = $false
+ $pinfo.Arguments = $arguments
+ $pinfo.WorkingDirectory = Get-Location
+ $p = New-Object System.Diagnostics.Process
+ $p.StartInfo = $pinfo
+ $p.Start() | Out-Null
+ $data = New-Object PSObject -Property @{
+ Process = $p
+ ReportFile = $reportFile
+ }
+ $rv = $runningProcesses.Add($data)
}
Function RunAllAllocationApplicationArgs($options, $validateoptions, $ust_test)
@@ -110,7 +202,7 @@ Function RunAllocationApplication($arg, $config, $arch_dir, $arch, $alloc, $opti
remove-item $dmp_2 -ErrorAction:SilentlyContinue | Out-Null
remove-item $log -ErrorAction:SilentlyContinue | Out-Null
remove-item $json -ErrorAction:SilentlyContinue | Out-Null
- RunCommand "$PSScriptRoot\$arch_dir\$CompilerDir\$config\$app_name" "--test `"$arg`" --type `"$alloc`" --dmp1 `"$dmp_1`" --dmp2 `"$dmp_2`" --log `"$log`" --json `"$json`""
+ RunCommandNow "$PSScriptRoot\$arch_dir\$CompilerDir\$config\$app_name" "--test `"$arg`" --type `"$alloc`" --dmp1 `"$dmp_1`" --dmp2 `"$dmp_2`" --log `"$log`" --json `"$json`""
}
RunAllocationApplicationChecks $validateoptions $base_name
@@ -122,6 +214,17 @@ Function RunAllocationApplicationChecks($validateoptions, $base_name)
$dmp_2 = "$DumpFolder\$($base_name)_2.dmp"
$json = "$DumpFolder\$base_name.json"
+ if($ConcurrentLimit -eq 1)
+ {
+ $verbose_arg = "--verbose"
+ $no_output_arg = ""
+ }
+ else
+ {
+ $verbose_arg = ""
+ $no_output_arg = "--no-output"
+ }
+
if($GenerateHeapLogs)
{
$dmp_1_full_log = "$DumpFolder\$($base_name)_1_full.log"
@@ -135,13 +238,15 @@ Function RunAllocationApplicationChecks($validateoptions, $base_name)
remove-item $dmp_2_full_diff_log -ErrorAction:SilentlyContinue | Out-Null
remove-item $dmp_2_debug_full_diff_log -ErrorAction:SilentlyContinue | Out-Null
- RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries --verbose --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --dumpfile `"$dmp_1`" --out `"$dmp_1_full_log`""
- RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries --verbose --heapdebug --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --symbols --dumpfile `"$dmp_1`" --out `"$dmp_1_debug_full_log`""
- RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries --verbose --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --dumpfile `"$dmp_2`" --basediffdumpfile `"$dmp_1`" --out `"$dmp_2_full_diff_log`""
- RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries --verbose --heapdebug --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --symbols --dumpfile `"$dmp_2`" --basediffdumpfile `"$dmp_1`" --out `"$dmp_2_debug_full_diff_log`""
+ RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries $verbose_arg --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --modules --dumpfile `"$dmp_1`" --out `"$dmp_1_full_log`""
+ RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries $verbose_arg --heapdebug --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --symbols --modules --dumpfile `"$dmp_1`" --out `"$dmp_1_debug_full_log`""
+ RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries $verbose_arg --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --modules --dumpfile `"$dmp_2`" --basediffdumpfile `"$dmp_1`" --out `"$dmp_2_full_diff_log`""
+ RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries $verbose_arg --heapdebug --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --symbols --modules --dumpfile `"$dmp_2`" --basediffdumpfile `"$dmp_1`" --out `"$dmp_2_debug_full_diff_log`""
}
- RunCommand "$ExeFolder\ValidateHeapEntries.exe" "--dmp1 `"$dmp_1`" --dmp2 `"$dmp_2`" --log `"$ResultFile`" --json `"$json`" $validateoptions"
+ $tempFile = New-TemporaryFile
+ Write-Verbose "Validating [$base_name]"
+ RunCommand "$ExeFolder\ValidateHeapEntries.exe" "$no_output_arg --dmp1 `"$dmp_1`" --dmp2 `"$dmp_2`" --log `"$tempFile`" --json `"$json`" $validateoptions" $tempFile
}
Function RunAllocationApplicationSingleDumpChecks($validateoptions, $base_name)
@@ -149,6 +254,17 @@ Function RunAllocationApplicationSingleDumpChecks($validateoptions, $base_name)
$dmp = "$DumpFolder\$($base_name).dmp"
$json = "$DumpFolder\$base_name.json"
+ if($ConcurrentLimit -eq 1)
+ {
+ $verbose_arg = "--verbose"
+ $no_output_arg = ""
+ }
+ else
+ {
+ $verbose_arg = ""
+ $no_output_arg = "--no-output"
+ }
+
if($GenerateHeapLogs)
{
$dmp_full_log = "$DumpFolder\$($base_name)_full.log"
@@ -158,11 +274,13 @@ Function RunAllocationApplicationSingleDumpChecks($validateoptions, $base_name)
remove-item $dmp_full_log -ErrorAction:SilentlyContinue | Out-Null
remove-item $dmp_debug_full_log -ErrorAction:SilentlyContinue | Out-Null
- RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries --verbose --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --dumpfile `"$dmp`" --out `"$dmp_full_log`""
- RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries --verbose --heapdebug --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --symbols --dumpfile `"$dmp`" --out `"$dmp_debug_full_log`""
+ RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries $verbose_arg --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --modules --dumpfile `"$dmp`" --out `"$dmp_full_log`""
+ RunCommand "$ExeFolder\MiniDumper.exe" "--disable-symbol-load-cancel-keyboard-check --heap --crtheap --heapentries $verbose_arg --heapdebug --heapstat all --heapgraph --nofilterheapentries --nomarknoncrtsystem --modules --symbols --dumpfile `"$dmp`" --out `"$dmp_debug_full_log`""
}
- RunCommand "$ExeFolder\ValidateHeapEntries.exe" "--dmp1 `"$dmp`" --log `"$ResultFile`" --json `"$json`" $validateoptions"
+ $tempFile = New-TemporaryFile
+ Write-Verbose "Validating [$base_name]"
+ RunCommand "$ExeFolder\ValidateHeapEntries.exe" "$no_output_arg --dmp1 `"$dmp`" --log `"$tempFile`" --json `"$json`" $validateoptions" $tempFile
}
Function RunStandardTests()
@@ -334,3 +452,5 @@ else
{
RunStandardTests
}
+
+WaitForAllProcessesToComplete
diff --git a/ValidateHeapEntries/ValidateHeapEntries.cpp b/ValidateHeapEntries/ValidateHeapEntries.cpp
index 4171685..715a35a 100644
--- a/ValidateHeapEntries/ValidateHeapEntries.cpp
+++ b/ValidateHeapEntries/ValidateHeapEntries.cpp
@@ -593,7 +593,7 @@ bool validate_allocation_graph_entries_are_not_marked_system(std::vector const& allocations)
+[[nodiscard]] bool validate_dump_file(bool stacktrace, bool skip_fake_offset_check, bool no_output, std::wstring const& dump_filename, std::wstring const& base_dump_filename, std::wostream* o_log, std::vector const& allocations)
{
*o_log << std::format(L"Processing dmp [{}]\n", dump_filename);
@@ -607,7 +607,7 @@ bool validate_allocation_graph_entries_are_not_marked_system(std::vector log;
- std::wostream* o_log{&std::wcout};
+ null_stream null_stream;
+ std::wostream* o_log{no_output ? &null_stream : &std::wcout};
if(!log_filename.empty())
{
log = std::make_unique(log_filename, std::ios_base::out | std::ios_base::app);
@@ -757,10 +763,10 @@ int main(int const argc, char* argv[])
return EXIT_FAILURE;
}
- auto successful = validate_dump_file(stacktrace, skip_fake_offset_check, dump_filename_1, {}, o_log, set.first_allocations);
+ auto successful = validate_dump_file(stacktrace, skip_fake_offset_check, no_output, dump_filename_1, {}, o_log, set.first_allocations);
if(successful && !dump_filename_2.empty())
{
- successful = validate_dump_file(stacktrace, skip_fake_offset_check, dump_filename_2, dump_filename_1, o_log, set.second_allocations);
+ successful = validate_dump_file(stacktrace, skip_fake_offset_check, no_output, dump_filename_2, dump_filename_1, o_log, set.second_allocations);
}
if(log)
diff --git a/ValidateHeapEntries/symbol_engine_ui.cpp b/ValidateHeapEntries/symbol_engine_ui.cpp
index 992943a..7082795 100644
--- a/ValidateHeapEntries/symbol_engine_ui.cpp
+++ b/ValidateHeapEntries/symbol_engine_ui.cpp
@@ -3,6 +3,11 @@
#include
#include
+symbol_engine_ui::symbol_engine_ui(bool const no_output)
+ : no_output_{no_output}
+{
+}
+
bool symbol_engine_ui::deferred_symbol_load_cancel([[maybe_unused]] std::wstring_view const& module_name)
{
return false;
@@ -17,7 +22,10 @@ void symbol_engine_ui::start_download(std::wstring_view const& module_name)
std::wstringstream ss;
ss << L"downloading " << module_name << L": ";
module_ = std::move(ss).str();
- std::wcerr << module_;
+ if(!no_output_)
+ {
+ std::wcerr << module_;
+ }
last_percent_.clear();
}
@@ -30,13 +38,16 @@ void symbol_engine_ui::download_percent(unsigned const percent)
std::wstringstream ss;
ss << percent << L"%";
- std::wcerr << ss.str();
+ if(!no_output_)
+ {
+ std::wcerr << ss.str();
+ }
last_percent_ = std::move(ss).str();
}
void symbol_engine_ui::download_complete()
{
- if (!last_percent_.empty() || !module_.empty())
+ if (!no_output_ && (!last_percent_.empty() || !module_.empty()))
{
std::wstring const clear(last_percent_.size() + module_.size(), L'\b');
std::wcerr << clear << std::wstring(last_percent_.size() + module_.size(), L' ') << clear;
@@ -47,7 +58,12 @@ void symbol_engine_ui::download_complete()
std::wostream& symbol_engine_ui::log_stream() const
{
- return std::wcerr;
+ if(!no_output_)
+ {
+ return std::wcerr;
+ }
+
+ return null_stream_;
}
bool symbol_engine_ui::symbol_load_debug() const
diff --git a/ValidateHeapEntries/symbol_engine_ui.h b/ValidateHeapEntries/symbol_engine_ui.h
index 720ae0f..3128c56 100644
--- a/ValidateHeapEntries/symbol_engine_ui.h
+++ b/ValidateHeapEntries/symbol_engine_ui.h
@@ -1,10 +1,11 @@
#pragma once
#include "DbgHelpUtils/i_symbol_load_callback.h"
+#include "DbgHelpUtils/null_stream.h"
class symbol_engine_ui : public dlg_help_utils::dbg_help::i_symbol_load_callback
{
public:
- symbol_engine_ui() = default;
+ symbol_engine_ui(bool no_output);
[[nodiscard]] bool deferred_symbol_load_cancel(std::wstring_view const& module_name) override;
void deferred_symbol_load_partial(std::wstring_view const& module_name) override;
@@ -16,6 +17,8 @@ class symbol_engine_ui : public dlg_help_utils::dbg_help::i_symbol_load_callback
[[nodiscard]] bool symbol_load_debug_memory() const override;
private:
+ bool no_output_;
std::wstring module_;
std::wstring last_percent_;
+ mutable null_stream null_stream_;
};