Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b37134bb36 |
@@ -10,3 +10,5 @@
|
|||||||
/output/
|
/output/
|
||||||
/webapp_gallery/.playwright-cli/
|
/webapp_gallery/.playwright-cli/
|
||||||
/logs/
|
/logs/
|
||||||
|
/deadlock_capture/
|
||||||
|
/*.zip
|
||||||
|
|||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$targetExe = "D:\ae\proj\Renderive\cmake-build-vs2022_debug\web_server\Renderive_Web_Server.exe"
|
||||||
|
$cdb = "D:\ae\ewdk\EWDK_22621_230929-1800\Program Files\Windows Kits\10\Debuggers\x64\cdb.exe"
|
||||||
|
$outputRoot = "D:\ae\proj\Renderive\deadlock_capture"
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $cdb))
|
||||||
|
{
|
||||||
|
throw "CDB not found: $cdb"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $targetExe))
|
||||||
|
{
|
||||||
|
throw "Target executable not found: $targetExe"
|
||||||
|
}
|
||||||
|
|
||||||
|
$processes = @(
|
||||||
|
Get-CimInstance Win32_Process |
|
||||||
|
Where-Object { $_.ExecutablePath -eq $targetExe }
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($processes.Count -eq 0)
|
||||||
|
{
|
||||||
|
throw "Renderive_Web_Server.exe is not running: $targetExe"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($processes.Count -ne 1)
|
||||||
|
{
|
||||||
|
$processes |
|
||||||
|
Select-Object ProcessId, Name, ExecutablePath |
|
||||||
|
Format-Table -AutoSize
|
||||||
|
throw "Multiple matching processes found. Stop the extra processes first."
|
||||||
|
}
|
||||||
|
|
||||||
|
$pidValue = [int]$processes[0].ProcessId
|
||||||
|
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||||
|
$outputDir = Join-Path $outputRoot $timestamp
|
||||||
|
$logPath = Join-Path $outputDir "Renderive_deadlock_$timestamp.txt"
|
||||||
|
$dumpPath = Join-Path $outputDir "Renderive_deadlock_$timestamp.dmp"
|
||||||
|
$commandPath = Join-Path $outputDir "cdb_commands.txt"
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
|
||||||
|
|
||||||
|
$cdbCommands = @"
|
||||||
|
.echo ============================================================
|
||||||
|
.echo Renderive deadlock capture
|
||||||
|
.echo ============================================================
|
||||||
|
.echo === PROCESS ===
|
||||||
|
|
|
||||||
|
.echo === ALL THREAD STACKS ===
|
||||||
|
~* kb
|
||||||
|
.echo === THREAD CPU TIME ===
|
||||||
|
!runaway 3
|
||||||
|
.echo === LOCKS ===
|
||||||
|
!locks
|
||||||
|
.echo === HANG ANALYSIS ===
|
||||||
|
!analyze -hang
|
||||||
|
.echo === FULL MEMORY DUMP ===
|
||||||
|
.dump /ma $dumpPath
|
||||||
|
.echo === CAPTURE FINISHED ===
|
||||||
|
q
|
||||||
|
"@
|
||||||
|
|
||||||
|
Set-Content -LiteralPath $commandPath -Value $cdbCommands -Encoding ASCII
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Target:"
|
||||||
|
Write-Host " $targetExe"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "PID:"
|
||||||
|
Write-Host " $pidValue"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Output:"
|
||||||
|
Write-Host " $outputDir"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Attaching CDB non-invasively..."
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$cdbArgs = @(
|
||||||
|
"-pv"
|
||||||
|
"-p"
|
||||||
|
$pidValue.ToString()
|
||||||
|
"-logo"
|
||||||
|
$logPath
|
||||||
|
"-cf"
|
||||||
|
$commandPath
|
||||||
|
)
|
||||||
|
|
||||||
|
& $cdb @cdbArgs
|
||||||
|
|
||||||
|
$cdbExitCode = $LASTEXITCODE
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "CDB exit code: $cdbExitCode"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
if (Test-Path -LiteralPath $logPath)
|
||||||
|
{
|
||||||
|
Write-Host "Log:"
|
||||||
|
Write-Host " $logPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path -LiteralPath $dumpPath)
|
||||||
|
{
|
||||||
|
$dump = Get-Item -LiteralPath $dumpPath
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Dump:"
|
||||||
|
Write-Host " $dumpPath"
|
||||||
|
Write-Host " Size: $([math]::Round($dump.Length / 1MB, 2) ) MB"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Capture complete."
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "common/Gallery_Scene_Interface.h"
|
#include "common/Gallery_Scene_Interface.h"
|
||||||
#include "render_2D/Gallery_Scene2D.h"
|
#include "render_2D/Gallery_Scene2D.h"
|
||||||
#include "render_3D/Gallery_Scene3D.h"
|
#include "render_3D/Gallery_Scene3D.h"
|
||||||
#include <renderive/scheduling/Scheduler.hpp>
|
|
||||||
#include <trantor/net/EventLoopThread.h>
|
#include <trantor/net/EventLoopThread.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -204,24 +203,31 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
scene->set_client_metrics(performance);
|
scene->set_client_metrics(performance);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
[[nodiscard]] std::unique_lock<std::mutex> acquire_foreground_lock() {
|
[[nodiscard]] std::unique_lock<std::mutex> acquire_scene_lock() {
|
||||||
return std::unique_lock<std::mutex>(mutex);
|
return std::unique_lock<std::mutex>(scene_mutex);
|
||||||
}
|
}
|
||||||
void disarm_automatic_render() {
|
void disarm_automatic_render_locked() {
|
||||||
++automatic_timer_revision;
|
++automatic_timer_revision;
|
||||||
if (automatic_timer == trantor::InvalidTimerId)
|
if (automatic_timer == trantor::InvalidTimerId)
|
||||||
return;
|
return;
|
||||||
automatic_render_loop()->invalidateTimer(automatic_timer);
|
automatic_render_loop()->invalidateTimer(automatic_timer);
|
||||||
automatic_timer = trantor::InvalidTimerId;
|
automatic_timer = trantor::InvalidTimerId;
|
||||||
}
|
}
|
||||||
void arm_automatic_render_locked() {
|
void disarm_automatic_render() {
|
||||||
|
std::lock_guard lock(state_mutex);
|
||||||
|
disarm_automatic_render_locked();
|
||||||
|
}
|
||||||
|
void arm_automatic_render() {
|
||||||
if (!automatic_low_latency)
|
if (!automatic_low_latency)
|
||||||
return;
|
return;
|
||||||
disarm_automatic_render();
|
const bool can_render = scene && scene->can_render_automatically();
|
||||||
if (!scene || !scene->can_render_automatically() || render_task_pending)
|
const auto interval = can_render
|
||||||
|
? std::chrono::nanoseconds(std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()))
|
||||||
|
: std::chrono::nanoseconds::zero();
|
||||||
|
std::lock_guard lock(state_mutex);
|
||||||
|
disarm_automatic_render_locked();
|
||||||
|
if (!can_render || automatic_render_running)
|
||||||
return;
|
return;
|
||||||
const auto interval = std::chrono::nanoseconds(
|
|
||||||
std::max<std::uint64_t>(1, scene->kernel_refresh_interval_ns()));
|
|
||||||
const auto deadline = last_render_started ? *last_render_started + interval : Clock::now();
|
const auto deadline = last_render_started ? *last_render_started + interval : Clock::now();
|
||||||
const auto delay = std::max(Clock::duration::zero(), deadline - Clock::now());
|
const auto delay = std::max(Clock::duration::zero(), deadline - Clock::now());
|
||||||
const std::uint64_t revision = automatic_timer_revision;
|
const std::uint64_t revision = automatic_timer_revision;
|
||||||
@@ -235,46 +241,46 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
void automatic_render_due(std::uint64_t revision) {
|
void automatic_render_due(std::uint64_t revision) {
|
||||||
std::uint64_t generation{};
|
std::uint64_t generation{};
|
||||||
{
|
{
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(state_mutex);
|
||||||
if (revision != automatic_timer_revision)
|
if (revision != automatic_timer_revision)
|
||||||
return;
|
return;
|
||||||
automatic_timer = trantor::InvalidTimerId;
|
automatic_timer = trantor::InvalidTimerId;
|
||||||
++automatic_timer_revision;
|
++automatic_timer_revision;
|
||||||
if (!automatic_low_latency || !scene || !scene->can_render_automatically() || render_task_pending)
|
if (!automatic_low_latency || automatic_render_running)
|
||||||
return;
|
return;
|
||||||
render_task_pending = true;
|
automatic_render_running = true;
|
||||||
generation = scene_generation;
|
generation = scene_generation;
|
||||||
}
|
}
|
||||||
auto self = shared_from_this();
|
run_automatic_render(generation);
|
||||||
try {
|
|
||||||
renderive::scheduling::enqueue_task([self = std::move(self), generation] {
|
|
||||||
self->run_automatic_render(generation);
|
|
||||||
});
|
|
||||||
} catch (...) {
|
|
||||||
std::lock_guard lock(mutex);
|
|
||||||
render_task_pending = false;
|
|
||||||
automatic_render_exception = ::renderive::error::capture(
|
|
||||||
"arming automatic gallery render", std::current_exception());
|
|
||||||
disarm_automatic_render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void run_automatic_render(std::uint64_t generation) {
|
void run_automatic_render(std::uint64_t generation) {
|
||||||
const auto started = Clock::now();
|
const auto started = Clock::now();
|
||||||
try {
|
try {
|
||||||
std::lock_guard lock(mutex);
|
auto scene_lock = acquire_scene_lock();
|
||||||
if (generation == scene_generation && scene && scene->can_render_automatically()) {
|
bool current_generation{};
|
||||||
const auto error = scene->render_latest_frame();
|
{
|
||||||
if (error == Gallery_Render_Result::none)
|
std::lock_guard lock(state_mutex);
|
||||||
last_render_started = started;
|
current_generation = generation == scene_generation;
|
||||||
}
|
}
|
||||||
render_task_pending = false;
|
if (current_generation && scene && scene->can_render_automatically()) {
|
||||||
arm_automatic_render_locked();
|
const auto error = scene->render_latest_frame();
|
||||||
|
if (error == Gallery_Render_Result::none) {
|
||||||
|
std::lock_guard lock(state_mutex);
|
||||||
|
if (generation == scene_generation)
|
||||||
|
last_render_started = started;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard lock(state_mutex);
|
||||||
|
automatic_render_running = false;
|
||||||
|
}
|
||||||
|
arm_automatic_render();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(state_mutex);
|
||||||
render_task_pending = false;
|
automatic_render_running = false;
|
||||||
automatic_render_exception = ::renderive::error::capture(
|
automatic_render_exception = ::renderive::error::capture(
|
||||||
"running automatic gallery render", std::current_exception());
|
"running automatic gallery render", std::current_exception());
|
||||||
disarm_automatic_render();
|
disarm_automatic_render_locked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static bool affects_render_schedule(const Web_Event& event) {
|
static bool affects_render_schedule(const Web_Event& event) {
|
||||||
@@ -297,11 +303,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
event);
|
event);
|
||||||
}
|
}
|
||||||
std::unique_ptr<Gallery_Scene_Interface> scene;
|
std::unique_ptr<Gallery_Scene_Interface> scene;
|
||||||
std::mutex mutex;
|
std::mutex scene_mutex;
|
||||||
|
std::mutex state_mutex;
|
||||||
std::optional<Clock::time_point> last_render_started;
|
std::optional<Clock::time_point> last_render_started;
|
||||||
std::uint64_t scene_generation{};
|
std::uint64_t scene_generation{};
|
||||||
bool automatic_low_latency{};
|
bool automatic_low_latency{};
|
||||||
bool render_task_pending{};
|
bool automatic_render_running{};
|
||||||
std::exception_ptr automatic_render_exception;
|
std::exception_ptr automatic_render_exception;
|
||||||
std::uint64_t session_id{};
|
std::uint64_t session_id{};
|
||||||
trantor::TimerId automatic_timer{trantor::InvalidTimerId};
|
trantor::TimerId automatic_timer{trantor::InvalidTimerId};
|
||||||
@@ -317,9 +324,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
Gallery_Protocol::error_json("未知的 gallery case 或 frame_mode", "case")
|
Gallery_Protocol::error_json("未知的 gallery case 或 frame_mode", "case")
|
||||||
};
|
};
|
||||||
scene = make_gallery_scene(session_id, open->case_id, open->frame_mode, automatic_low_latency);
|
scene = make_gallery_scene(session_id, open->case_id, open->frame_mode, automatic_low_latency);
|
||||||
++scene_generation;
|
{
|
||||||
last_render_started.reset();
|
std::lock_guard lock(state_mutex);
|
||||||
arm_automatic_render_locked();
|
++scene_generation;
|
||||||
|
last_render_started.reset();
|
||||||
|
}
|
||||||
|
arm_automatic_render();
|
||||||
return Web_Response{
|
return Web_Response{
|
||||||
Web_Response_Type::Json,
|
Web_Response_Type::Json,
|
||||||
Gallery_Protocol::case_json_from_controls(scene->case_id(), scene->controls().dump(),
|
Gallery_Protocol::case_json_from_controls(scene->case_id(), scene->controls().dump(),
|
||||||
@@ -335,7 +345,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
};
|
};
|
||||||
if (request.kind == Gallery_Request_Kind::Observe) {
|
if (request.kind == Gallery_Request_Kind::Observe) {
|
||||||
if (update_client_metrics(request.message))
|
if (update_client_metrics(request.message))
|
||||||
arm_automatic_render_locked();
|
arm_automatic_render();
|
||||||
return Web_Response{
|
return Web_Response{
|
||||||
Web_Response_Type::Json,
|
Web_Response_Type::Json,
|
||||||
Gallery_Protocol::observer_json(
|
Gallery_Protocol::observer_json(
|
||||||
@@ -344,7 +354,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
}
|
}
|
||||||
if (request.kind == Gallery_Request_Kind::Refresh) {
|
if (request.kind == Gallery_Request_Kind::Refresh) {
|
||||||
if (update_client_metrics(request.message))
|
if (update_client_metrics(request.message))
|
||||||
arm_automatic_render_locked();
|
arm_automatic_render();
|
||||||
return Web_Response{
|
return Web_Response{
|
||||||
Web_Response_Type::Json,
|
Web_Response_Type::Json,
|
||||||
Gallery_Protocol::case_json_from_controls(
|
Gallery_Protocol::case_json_from_controls(
|
||||||
@@ -398,9 +408,12 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
const std::string id = scene->case_id();
|
const std::string id = scene->case_id();
|
||||||
const auto mode = scene->frame_mode();
|
const auto mode = scene->frame_mode();
|
||||||
scene = make_gallery_scene(session_id, id, mode, automatic_low_latency);
|
scene = make_gallery_scene(session_id, id, mode, automatic_low_latency);
|
||||||
++scene_generation;
|
{
|
||||||
last_render_started.reset();
|
std::lock_guard lock(state_mutex);
|
||||||
arm_automatic_render_locked();
|
++scene_generation;
|
||||||
|
last_render_started.reset();
|
||||||
|
}
|
||||||
|
arm_automatic_render();
|
||||||
return Web_Response{
|
return Web_Response{
|
||||||
Web_Response_Type::Json,
|
Web_Response_Type::Json,
|
||||||
Gallery_Protocol::case_json_from_controls(id, scene->controls().dump(),
|
Gallery_Protocol::case_json_from_controls(id, scene->controls().dump(),
|
||||||
@@ -428,7 +441,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
const auto request_started = std::chrono::steady_clock::now();
|
const auto request_started = std::chrono::steady_clock::now();
|
||||||
std::optional<std::string> pixels;
|
std::optional<std::string> pixels;
|
||||||
{
|
{
|
||||||
auto lock = acquire_foreground_lock();
|
auto lock = acquire_scene_lock();
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
const auto encode_started = std::chrono::steady_clock::now();
|
const auto encode_started = std::chrono::steady_clock::now();
|
||||||
@@ -446,7 +459,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
{
|
{
|
||||||
std::exception_ptr exception;
|
std::exception_ptr exception;
|
||||||
{
|
{
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(state_mutex);
|
||||||
exception = std::exchange(automatic_render_exception, {});
|
exception = std::exchange(automatic_render_exception, {});
|
||||||
}
|
}
|
||||||
if (exception)
|
if (exception)
|
||||||
@@ -458,7 +471,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
const bool reschedule = affects_render_schedule(event);
|
const bool reschedule = affects_render_schedule(event);
|
||||||
std::optional<Web_Response> response;
|
std::optional<Web_Response> response;
|
||||||
{
|
{
|
||||||
auto lock = acquire_foreground_lock();
|
auto lock = acquire_scene_lock();
|
||||||
response = std::visit(
|
response = std::visit(
|
||||||
[this](const auto& value) -> std::optional<Web_Response> {
|
[this](const auto& value) -> std::optional<Web_Response> {
|
||||||
using T = std::decay_t<decltype(value)>;
|
using T = std::decay_t<decltype(value)>;
|
||||||
@@ -484,7 +497,7 @@ struct Gallery_Plot_Session::Impl : std::enable_shared_from_this<Gallery_Plot_Se
|
|||||||
},
|
},
|
||||||
event);
|
event);
|
||||||
if (reschedule)
|
if (reschedule)
|
||||||
arm_automatic_render_locked();
|
arm_automatic_render();
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user