[development] Consolidated programmatic profiler capture API (#4969)
Merged overlapping profiler EBuses/Interfaces into AzCore Fixed ambiguous LogLevel type in some unity file scenarios Added cvar/console access for profiler capture Added utilities for reflecting AZ::Interfaces through the BehaviorContext Included profiler system sample python script Signed-off-by: AMZN-ScottR 24445312+AMZN-ScottR@users.noreply.github.com
This commit is contained in:
@@ -7,4 +7,63 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Debug/ProfilerBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
AZStd::string GenerateOutputFile(const char* nameHint)
|
||||
{
|
||||
AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation();
|
||||
return AZStd::string::format("%s/capture_%s_%lld.json", captureOutput.c_str(), nameHint, AZStd::GetTimeNowSecond());
|
||||
}
|
||||
|
||||
void ProfilerCaptureFrame([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
|
||||
{
|
||||
AZStd::string captureFile = GenerateOutputFile("single");
|
||||
AZLOG_INFO("Setting capture file to %s", captureFile.c_str());
|
||||
profilerSystem->CaptureFrame(captureFile);
|
||||
}
|
||||
}
|
||||
AZ_CONSOLEFREEFUNC(ProfilerCaptureFrame, AZ::ConsoleFunctorFlags::DontReplicate, "Capture a single frame of profiling data");
|
||||
|
||||
void ProfilerStartCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
|
||||
{
|
||||
AZStd::string captureFile = GenerateOutputFile("multi");
|
||||
AZLOG_INFO("Setting capture file to %s", captureFile.c_str());
|
||||
profilerSystem->StartCapture(AZStd::move(captureFile));
|
||||
}
|
||||
}
|
||||
AZ_CONSOLEFREEFUNC(ProfilerStartCapture, AZ::ConsoleFunctorFlags::DontReplicate, "Start a multi-frame capture of profiling data");
|
||||
|
||||
void ProfilerEndCapture([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
if (auto profilerSystem = ProfilerSystemInterface::Get(); profilerSystem)
|
||||
{
|
||||
profilerSystem->EndCapture();
|
||||
}
|
||||
}
|
||||
AZ_CONSOLEFREEFUNC(ProfilerEndCapture, AZ::ConsoleFunctorFlags::DontReplicate, "End and dump an in-progress continuous capture");
|
||||
|
||||
AZ::IO::FixedMaxPathString GetProfilerCaptureLocation()
|
||||
{
|
||||
AZ::IO::FixedMaxPathString captureOutput;
|
||||
if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
|
||||
{
|
||||
settingsRegistry->Get(captureOutput, RegistryKey_ProfilerCaptureLocation);
|
||||
}
|
||||
|
||||
if (captureOutput.empty())
|
||||
{
|
||||
captureOutput = ProfilerCaptureLocationFallback;
|
||||
}
|
||||
|
||||
return captureOutput;
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
|
||||
@@ -9,11 +9,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Debug
|
||||
{
|
||||
//! settings registry entry for specifying where to output profiler captures
|
||||
static constexpr const char* RegistryKey_ProfilerCaptureLocation = "/O3DE/AzCore/Debug/Profiler/CaptureLocation";
|
||||
|
||||
//! fallback value in the event the settings registry isn't ready or doesn't contain the key
|
||||
static constexpr const char* ProfilerCaptureLocationFallback = "@user@/Profiler";
|
||||
|
||||
/**
|
||||
* ProfilerNotifications provides a profiler event interface that can be used to update listeners on profiler status
|
||||
*/
|
||||
@@ -23,32 +32,38 @@ namespace AZ
|
||||
public:
|
||||
virtual ~ProfilerNotifications() = default;
|
||||
|
||||
virtual void OnProfileSystemInitialized() = 0;
|
||||
//! Notify when the current profiler capture is finished
|
||||
//! @param result Set to true if it's finished successfully
|
||||
//! @param info The output file path or error information which depends on the return.
|
||||
virtual void OnCaptureFinished(bool result, const AZStd::string& info) = 0;
|
||||
};
|
||||
using ProfilerNotificationBus = AZ::EBus<ProfilerNotifications>;
|
||||
|
||||
enum class ProfileFrameAdvanceType
|
||||
{
|
||||
Game,
|
||||
Render,
|
||||
Default = Game
|
||||
};
|
||||
|
||||
/**
|
||||
* ProfilerRequests provides an interface for making profiling system requests
|
||||
*/
|
||||
class ProfilerRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
// Allow multiple threads to concurrently make requests
|
||||
using MutexType = AZStd::mutex;
|
||||
|
||||
AZ_RTTI(ProfilerRequests, "{90AEC117-14C1-4BAE-9704-F916E49EF13F}");
|
||||
virtual ~ProfilerRequests() = default;
|
||||
|
||||
virtual bool IsActive() = 0;
|
||||
virtual void FrameAdvance(ProfileFrameAdvanceType type) = 0;
|
||||
//! Getter/setter for the profiler active state
|
||||
virtual bool IsActive() const = 0;
|
||||
virtual void SetActive(bool active) = 0;
|
||||
|
||||
//! Capture a single frame of profiling data
|
||||
virtual bool CaptureFrame(const AZStd::string& outputFilePath) = 0;
|
||||
|
||||
//! Starting/ending a multi-frame capture of profiling data
|
||||
virtual bool StartCapture(AZStd::string outputFilePath) = 0;
|
||||
virtual bool EndCapture() = 0;
|
||||
};
|
||||
using ProfilerRequestBus = AZ::EBus<ProfilerRequests>;
|
||||
}
|
||||
}
|
||||
|
||||
using ProfilerSystemInterface = AZ::Interface<ProfilerRequests>;
|
||||
|
||||
//! helper function for getting the profiler capture location from the settings registry that
|
||||
//! includes fallback handing in the event the registry value can't be determined
|
||||
AZ::IO::FixedMaxPathString GetProfilerCaptureLocation();
|
||||
} // namespace Debug
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Debug/ProfilerBus.h>
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/RTTI/BehaviorInterfaceProxy.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
static constexpr const char* ProfilerScriptCategory = "Profiler";
|
||||
static constexpr const char* ProfilerScriptModule = "debug";
|
||||
static constexpr AZ::Script::Attributes::ScopeFlags ProfilerScriptScope = AZ::Script::Attributes::ScopeFlags::Automation;
|
||||
|
||||
class ProfilerNotificationBusHandler final
|
||||
: public ProfilerNotificationBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
{
|
||||
public:
|
||||
AZ_EBUS_BEHAVIOR_BINDER(ProfilerNotificationBusHandler, "{44161459-B816-4876-95A4-BA16DEC767D6}", AZ::SystemAllocator,
|
||||
OnCaptureFinished
|
||||
);
|
||||
|
||||
void OnCaptureFinished(bool result, const AZStd::string& info) override
|
||||
{
|
||||
Call(FN_OnCaptureFinished, result, info);
|
||||
}
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<ProfilerNotificationBus>("ProfilerNotificationBus")
|
||||
->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
|
||||
->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
|
||||
->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope)
|
||||
->Handler<ProfilerNotificationBusHandler>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ProfilerSystemScriptProxy
|
||||
: public BehaviorInterfaceProxy<ProfilerRequests>
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ProfilerSystemScriptProxy, "{D671FB70-8B09-4C3A-96CD-06A339F3138E}", BehaviorInterfaceProxy<ProfilerRequests>);
|
||||
|
||||
AZ_BEHAVIOR_INTERFACE(ProfilerSystemScriptProxy, ProfilerRequests);
|
||||
};
|
||||
|
||||
void ProfilerReflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->ConstantProperty("g_ProfilerSystem", ProfilerSystemScriptProxy::GetProxy)
|
||||
->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
|
||||
->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
|
||||
->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope);
|
||||
|
||||
behaviorContext->Class<ProfilerSystemScriptProxy>("ProfilerSystemInterface")
|
||||
->Attribute(AZ::Script::Attributes::Category, ProfilerScriptCategory)
|
||||
->Attribute(AZ::Script::Attributes::Module, ProfilerScriptModule)
|
||||
->Attribute(AZ::Script::Attributes::Scope, ProfilerScriptScope)
|
||||
|
||||
->Method("IsValid", &ProfilerSystemScriptProxy::IsValid)
|
||||
|
||||
->Method("GetCaptureLocation",
|
||||
[](ProfilerSystemScriptProxy*) -> AZStd::string
|
||||
{
|
||||
AZ::IO::FixedMaxPathString captureOutput = GetProfilerCaptureLocation();
|
||||
return AZStd::string(captureOutput.c_str(), captureOutput.length());
|
||||
})
|
||||
|
||||
->Method("IsActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::IsActive>())
|
||||
->Method("SetActive", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::SetActive>())
|
||||
|
||||
->Method("CaptureFrame", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::CaptureFrame>())
|
||||
|
||||
->Method("StartCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::StartCapture>())
|
||||
->Method("EndCapture", ProfilerSystemScriptProxy::WrapMethod<&ProfilerRequests::EndCapture>());
|
||||
}
|
||||
|
||||
ProfilerNotificationBusHandler::Reflect(context);
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
|
||||
namespace Debug
|
||||
{
|
||||
//! Reflects the profiler bus script bindings
|
||||
void ProfilerReflect(AZ::ReflectContext* context);
|
||||
} // namespace Debug
|
||||
} // namespace AZ
|
||||
@@ -27,26 +27,21 @@
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/std/chrono/chrono.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
namespace Debug
|
||||
struct StackFrame;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
struct StackFrame;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
bool AttachDebugger();
|
||||
bool IsDebuggerPresent();
|
||||
void HandleExceptions(bool isEnabled);
|
||||
void DebugBreak();
|
||||
bool AttachDebugger();
|
||||
bool IsDebuggerPresent();
|
||||
void HandleExceptions(bool isEnabled);
|
||||
void DebugBreak();
|
||||
#endif
|
||||
void Terminate(int exitCode);
|
||||
}
|
||||
void Terminate(int exitCode);
|
||||
}
|
||||
|
||||
using namespace AZ::Debug;
|
||||
|
||||
namespace DebugInternal
|
||||
{
|
||||
// other threads can trigger fatals and errors, but the same thread should not, to avoid stack overflow.
|
||||
@@ -60,7 +55,7 @@ namespace AZ
|
||||
// Globals
|
||||
const int g_maxMessageLength = 4096;
|
||||
static const char* g_dbgSystemWnd = "System";
|
||||
Trace Debug::g_tracer;
|
||||
Trace g_tracer;
|
||||
void* g_exceptionInfo = nullptr;
|
||||
|
||||
// Environment var needed to track ignored asserts across systems and disable native UI under certain conditions
|
||||
@@ -616,4 +611,4 @@ namespace AZ
|
||||
val.Set(level);
|
||||
}
|
||||
}
|
||||
} // namspace AZ
|
||||
} // namspace AZ::Debug
|
||||
|
||||
@@ -156,7 +156,10 @@ namespace AZ
|
||||
void StreamerComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
bool isEnabled = false;
|
||||
AZ::Debug::ProfilerRequestBus::BroadcastResult(isEnabled, &AZ::Debug::ProfilerRequests::IsActive);
|
||||
if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem)
|
||||
{
|
||||
isEnabled = profilerSystem->IsActive();
|
||||
}
|
||||
|
||||
if (isEnabled)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/typetraits/function_traits.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
/**
|
||||
* Utility class for reflecting an AZ::Interface through the BehaviorContext
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* class MyInterface
|
||||
* {
|
||||
* public:
|
||||
* AZ_RTTI(MyInterface, "{BADDF000D-CDCD-CDCD-CDCD-BAAAADF0000D}");
|
||||
* virtual ~MyInterface() = default;
|
||||
*
|
||||
* virtual AZStd::string Foo() = 0;
|
||||
* virtual void Bar(float x, float y) = 0;
|
||||
* };
|
||||
*
|
||||
* class MySystemProxy
|
||||
* : public BehaviorInterfaceProxy<MyInterface>
|
||||
* {
|
||||
* public:
|
||||
* AZ_RTTI(MySystemProxy, "{CDCDCDCD-BAAD-BADD-F00D-CDCDCDCDCDCD}", BehaviorInterfaceProxy<MyInterface>);
|
||||
* AZ_BEHAVIOR_INTERFACE(MySystemProxy, MyInterface);
|
||||
* };
|
||||
*
|
||||
* void Reflect(AZ::ReflectContext* context)
|
||||
* {
|
||||
* if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
* {
|
||||
* behaviorContext->ConstantProperty("g_MySystem", MySystemProxy::GetProxy)
|
||||
* ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
* ->Attribute(AZ::Script::Attributes::Module, "MyModule");
|
||||
*
|
||||
* behaviorContext->Class<MySystemProxy>("MySystemInterface")
|
||||
* ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
* ->Attribute(AZ::Script::Attributes::Module, "MyModule")
|
||||
*
|
||||
* ->Method("Foo", MySystemProxy::WrapMethod<&MyInterface::Foo>())
|
||||
* ->Method("Bar", MySystemProxy::WrapMethod<&MyInterface::Bar>());
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
template<typename T>
|
||||
class BehaviorInterfaceProxy
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(BehaviorInterfaceProxy, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(BehaviorInterfaceProxy<T>, "{E7CC8D27-4499-454E-A7DF-3F72FBECD30D}");
|
||||
|
||||
BehaviorInterfaceProxy() = default;
|
||||
virtual ~BehaviorInterfaceProxy() = default;
|
||||
|
||||
//! Stores the instance which will use the provided shared_ptr deleter when the reference count hits zero
|
||||
BehaviorInterfaceProxy(AZStd::shared_ptr<T> sharedInstance)
|
||||
: m_instance(AZStd::move(sharedInstance))
|
||||
{
|
||||
}
|
||||
|
||||
//! Stores the instance which will perform a no-op deleter when the reference count hits zero
|
||||
BehaviorInterfaceProxy(T* rawIntance)
|
||||
: m_instance(rawIntance, [](T*) {})
|
||||
{
|
||||
}
|
||||
|
||||
//! Returns if the m_instance shared pointer is non-nullptr
|
||||
bool IsValid() const { return m_instance; }
|
||||
|
||||
protected:
|
||||
//! Internal access for use in the derived GetProxy function
|
||||
static T* GetInstance()
|
||||
{
|
||||
T* interfacePtr = AZ::Interface<T>::Get();
|
||||
AZ_Warning("BehaviorInterfaceProxy", interfacePtr,
|
||||
"There is currently no global %s registered with an AZ Interface<T>",
|
||||
AzTypeInfo<T>::Name()
|
||||
);
|
||||
// Don't delete the global instance, it is not owned by the behavior context
|
||||
return interfacePtr;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
struct MethodWrapper
|
||||
{
|
||||
template<typename Proxy, auto Method>
|
||||
static auto WrapMethod()
|
||||
{
|
||||
using ReturnType = AZStd::function_traits_get_result_t<AZStd::remove_cvref_t<decltype(Method)>>;
|
||||
return [](Proxy* proxy, Args... params) -> ReturnType
|
||||
{
|
||||
if (proxy && proxy->IsValid())
|
||||
{
|
||||
return AZStd::invoke(Method, proxy->m_instance, AZStd::forward<Args>(params)...);
|
||||
}
|
||||
return ReturnType();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
AZStd::shared_ptr<T> m_instance;
|
||||
};
|
||||
|
||||
#define AZ_BEHAVIOR_INTERFACE(ProxyType, InterfaceType) \
|
||||
static ProxyType GetProxy() { return GetInstance(); } \
|
||||
template<auto Method> \
|
||||
static auto WrapMethod() { \
|
||||
using FuncTraits = AZStd::function_traits<AZStd::remove_cvref_t<decltype(Method)>>; \
|
||||
return FuncTraits::template expand_args<MethodWrapper>::template WrapMethod<ProxyType, Method>(); \
|
||||
} \
|
||||
ProxyType() = default; \
|
||||
ProxyType(AZStd::shared_ptr<InterfaceType> sharedInstance) : BehaviorInterfaceProxy(sharedInstance) {} \
|
||||
ProxyType(InterfaceType* rawIntance) : BehaviorInterfaceProxy(rawIntance) {}
|
||||
} // namespace AZ
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Debug/ProfilerReflection.h>
|
||||
#include <AzCore/Debug/TraceReflection.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Math/MathReflection.h>
|
||||
@@ -925,6 +926,7 @@ void ScriptSystemComponent::Reflect(ReflectContext* reflection)
|
||||
// reflect default entity
|
||||
MathReflect(behaviorContext);
|
||||
ScriptDebug::Reflect(behaviorContext);
|
||||
Debug::ProfilerReflect(behaviorContext);
|
||||
Debug::TraceReflect(behaviorContext);
|
||||
|
||||
behaviorContext->Class<PlatformID>("Platform")
|
||||
|
||||
@@ -108,6 +108,8 @@ set(FILES
|
||||
Debug/Profiler.inl
|
||||
Debug/Profiler.h
|
||||
Debug/ProfilerBus.h
|
||||
Debug/ProfilerReflection.cpp
|
||||
Debug/ProfilerReflection.h
|
||||
Debug/StackTracer.h
|
||||
Debug/EventTrace.h
|
||||
Debug/EventTrace.cpp
|
||||
@@ -456,6 +458,7 @@ set(FILES
|
||||
RTTI/BehaviorContext.h
|
||||
RTTI/BehaviorContextUtilities.h
|
||||
RTTI/BehaviorContextUtilities.cpp
|
||||
RTTI/BehaviorInterfaceProxy.h
|
||||
RTTI/BehaviorObjectSignals.h
|
||||
RTTI/TypeSafeIntegral.h
|
||||
Script/ScriptAsset.cpp
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);
|
||||
@@ -26,94 +26,91 @@ namespace AZ
|
||||
|
||||
constexpr int g_maxMessageLength = 4096;
|
||||
|
||||
namespace Debug
|
||||
namespace Platform
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
bool IsDebuggerPresent()
|
||||
bool IsDebuggerPresent()
|
||||
{
|
||||
return ::IsDebuggerPresent() ? true : false;
|
||||
}
|
||||
|
||||
void HandleExceptions(bool isEnabled)
|
||||
{
|
||||
if (isEnabled)
|
||||
{
|
||||
return ::IsDebuggerPresent() ? true : false;
|
||||
g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler);
|
||||
}
|
||||
|
||||
void HandleExceptions(bool isEnabled)
|
||||
else
|
||||
{
|
||||
if (isEnabled)
|
||||
{
|
||||
g_previousExceptionHandler = ::SetUnhandledExceptionFilter(&ExceptionHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
::SetUnhandledExceptionFilter(g_previousExceptionHandler);
|
||||
g_previousExceptionHandler = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool AttachDebugger()
|
||||
{
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Launch vsjitdebugger.exe, this app is always present in System32 folder
|
||||
// with an installation of any version of visual studio.
|
||||
// It will open a debugging dialog asking the user what debugger to use
|
||||
|
||||
STARTUPINFOW startupInfo = {0};
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
PROCESS_INFORMATION processInfo = {0};
|
||||
|
||||
wchar_t cmdline[MAX_PATH];
|
||||
swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId());
|
||||
bool success = ::CreateProcessW(
|
||||
NULL, // No module name (use command line)
|
||||
cmdline, // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // No handle inheritance
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&startupInfo, // Pointer to STARTUPINFO structure
|
||||
&processInfo); // Pointer to PROCESS_INFORMATION structure
|
||||
|
||||
if (success)
|
||||
{
|
||||
::WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||
::CloseHandle(processInfo.hProcess);
|
||||
::CloseHandle(processInfo.hThread);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DebugBreak()
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
#endif // AZ_ENABLE_DEBUG_TOOLS
|
||||
|
||||
void Terminate(int exitCode)
|
||||
{
|
||||
TerminateProcess(GetCurrentProcess(), exitCode);
|
||||
}
|
||||
|
||||
void OutputToDebugger([[maybe_unused]] const char* window, const char* message)
|
||||
{
|
||||
AZStd::fixed_wstring<g_maxMessageLength> tmpW;
|
||||
if(window)
|
||||
{
|
||||
AZStd::to_wstring(tmpW, window);
|
||||
tmpW += L": ";
|
||||
OutputDebugStringW(tmpW.c_str());
|
||||
tmpW.clear();
|
||||
}
|
||||
AZStd::to_wstring(tmpW, message);
|
||||
OutputDebugStringW(tmpW.c_str());
|
||||
::SetUnhandledExceptionFilter(g_previousExceptionHandler);
|
||||
g_previousExceptionHandler = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AttachDebugger()
|
||||
{
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Launch vsjitdebugger.exe, this app is always present in System32 folder
|
||||
// with an installation of any version of visual studio.
|
||||
// It will open a debugging dialog asking the user what debugger to use
|
||||
|
||||
STARTUPINFOW startupInfo = {0};
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
PROCESS_INFORMATION processInfo = {0};
|
||||
|
||||
wchar_t cmdline[MAX_PATH];
|
||||
swprintf_s(cmdline, L"vsjitdebugger.exe -p %li", ::GetCurrentProcessId());
|
||||
bool success = ::CreateProcessW(
|
||||
NULL, // No module name (use command line)
|
||||
cmdline, // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // No handle inheritance
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&startupInfo, // Pointer to STARTUPINFO structure
|
||||
&processInfo); // Pointer to PROCESS_INFORMATION structure
|
||||
|
||||
if (success)
|
||||
{
|
||||
::WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||
::CloseHandle(processInfo.hProcess);
|
||||
::CloseHandle(processInfo.hThread);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DebugBreak()
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
#endif // AZ_ENABLE_DEBUG_TOOLS
|
||||
|
||||
void Terminate(int exitCode)
|
||||
{
|
||||
TerminateProcess(GetCurrentProcess(), exitCode);
|
||||
}
|
||||
|
||||
void OutputToDebugger([[maybe_unused]] const char* window, const char* message)
|
||||
{
|
||||
AZStd::fixed_wstring<g_maxMessageLength> tmpW;
|
||||
if(window)
|
||||
{
|
||||
AZStd::to_wstring(tmpW, window);
|
||||
tmpW += L": ";
|
||||
OutputDebugStringW(tmpW.c_str());
|
||||
tmpW.clear();
|
||||
}
|
||||
AZStd::to_wstring(tmpW, message);
|
||||
OutputDebugStringW(tmpW.c_str());
|
||||
}
|
||||
} // namespace Platform
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
|
||||
@@ -211,4 +208,4 @@ namespace AZ
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
} // namspace AZ::Debug
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <CryAssert.h>
|
||||
|
||||
namespace AZ
|
||||
namespace AZ::Debug
|
||||
{
|
||||
AZ_CVAR_EXTERNED(int, bg_traceLogLevel);
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
if(!hasSetCVar && ready)
|
||||
{
|
||||
// AZ logging only has a concept of 3 levels (error, warning, info) but cry logging has 4 levels (..., messaging). If info level is set, we'll turn on messaging as well
|
||||
int logLevel = AZ::bg_traceLogLevel == AZ::Debug::LogLevel::Info ? 4 : AZ::bg_traceLogLevel;
|
||||
int logLevel = AZ::Debug::bg_traceLogLevel == AZ::Debug::LogLevel::Info ? 4 : AZ::Debug::bg_traceLogLevel;
|
||||
|
||||
gEnv->pConsole->GetCVar("log_WriteToFileVerbosity")->Set(logLevel);
|
||||
hasSetCVar = true;
|
||||
|
||||
Reference in New Issue
Block a user