melon 🍉🍉🍉
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/.vs
|
/.vs
|
||||||
x64
|
bin
|
||||||
|
obj
|
||||||
22
Peak.sln
Normal file
22
Peak.sln
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.12.35506.116 d17.12
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Peak", "Peak\Peak.csproj", "{132011FB-CD98-4500-B7A7-B5BB0CD3BFA4}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{132011FB-CD98-4500-B7A7-B5BB0CD3BFA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{132011FB-CD98-4500-B7A7-B5BB0CD3BFA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{132011FB-CD98-4500-B7A7-B5BB0CD3BFA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{132011FB-CD98-4500-B7A7-B5BB0CD3BFA4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
550
Peak/Peak.csproj
Normal file
550
Peak/Peak.csproj
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<RootNamespace>Peak</RootNamespace>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||||
|
<FileVersion>1.0.0.0</FileVersion>
|
||||||
|
<NeutralLanguage>en-US</NeutralLanguage>
|
||||||
|
<AssemblyName>Peak</AssemblyName>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Ashley.MeshSplitter">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Ashley.MeshSplitter.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp-firstpass">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Assembly-CSharp">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="com.rlabrecque.steamworks.net">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\com.rlabrecque.steamworks.net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="DemiLib">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\DemiLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="DOTween">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\DOTween.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="DOTweenPro">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\DOTweenPro.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HBAO.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\HBAO.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HBAO.Universal.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\HBAO.Universal.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Photon3Unity3D">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Photon3Unity3D.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonChat">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonChat.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonRealtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonRealtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonUnityNetworking.Demos">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonUnityNetworking.Demos.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonUnityNetworking">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonUnityNetworking.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonUnityNetworking.Utilities">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonUnityNetworking.Utilities.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonVoice.API">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonVoice.API.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonVoice">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonVoice.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="PhotonVoice.PUN">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\PhotonVoice.PUN.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="pworld">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\pworld.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="sc.posteffects.runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\sc.posteffects.runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sirenix.OdinInspector.Attributes">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Sirenix.OdinInspector.Attributes.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sirenix.OdinInspector.Modules.Unity.Addressables">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Sirenix.OdinInspector.Modules.Unity.Addressables.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sirenix.OdinInspector.Modules.UnityLocalization">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Sirenix.OdinInspector.Modules.UnityLocalization.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sirenix.Serialization.Config">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Sirenix.Serialization.Config.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sirenix.Serialization">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Sirenix.Serialization.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sirenix.Utilities">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Sirenix.Utilities.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Tayx.Graphy">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Tayx.Graphy.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="unity-websocket-sharp">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\unity-websocket-sharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Addressables">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Addressables.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.AI.Navigation">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.AI.Navigation.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Animation.Rigging">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Animation.Rigging.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Animation.Rigging.DocCodeExamples">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Animation.Rigging.DocCodeExamples.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Burst">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Burst.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Burst.Unsafe">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Burst.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Cinemachine">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Cinemachine.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Collections">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Collections.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Collections.LowLevel.ILSupport">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.InputSystem">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.InputSystem.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.InputSystem.ForUI">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.InputSystem.ForUI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.InputSystem.RebindingUI">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.InputSystem.RebindingUI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.InternalAPIEngineBridge.013">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.InternalAPIEngineBridge.013.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Localization">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Localization.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Mathematics">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Mathematics.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.MemoryProfiler">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.MemoryProfiler.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Multiplayer.Center.Common">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Multiplayer.Center.Common.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Multiplayer.Playmode.Common.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Multiplayer.Playmode.Common.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Multiplayer.Playmode">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Multiplayer.Playmode.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Networking.Transport">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Networking.Transport.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Profiling.Core">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Profiling.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Rendering.LightTransport.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Rendering.LightTransport.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipeline.Universal.ShaderLibrary">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipeline.Universal.ShaderLibrary.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Core.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Core.Runtime.Shared">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Core.Runtime.Shared.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Core.ShaderLibrary">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.GPUDriven.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.GPUDriven.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Universal.2D.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Universal.2D.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Universal.Config.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Universal.Config.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Universal.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Universal.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.RenderPipelines.Universal.Shaders">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.RenderPipelines.Universal.Shaders.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.ResourceManager">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.ResourceManager.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.ScriptableBuildPipeline">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.ScriptableBuildPipeline.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Authentication">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Authentication.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Authentication.PlayerAccounts">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Authentication.PlayerAccounts.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Analytics">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Analytics.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Components">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Components.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Configuration">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Configuration.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Device">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Device.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Environments">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Environments.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Environments.Internal">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Environments.Internal.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Internal">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Internal.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Networking">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Networking.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Registration">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Registration.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Scheduler">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Scheduler.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Telemetry">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Telemetry.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Core.Threading">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Core.Threading.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Multiplayer">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Multiplayer.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.QoS">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.QoS.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Services.Wire.Internal">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Services.Wire.Internal.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Splines">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Splines.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.TextMeshPro">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.TextMeshPro.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.Timeline">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.Timeline.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Unity.VisualEffectGraph.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Unity.VisualEffectGraph.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AccessibilityModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AccessibilityModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AIModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AMDModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AMDModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AndroidJNIModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AndroidJNIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AnimationModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ARModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ARModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AssetBundleModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AudioModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClothModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ClothModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClusterInputModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ClusterInputModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ClusterRendererModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ClusterRendererModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ContentLoadModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ContentLoadModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CrashReportingModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.CrashReportingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.DirectorModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.DirectorModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.DSPGraphModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.DSPGraphModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GameCenterModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.GameCenterModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GIModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.GIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GraphicsStateCollectionSerializerModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.GraphicsStateCollectionSerializerModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.GridModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.GridModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.HierarchyCoreModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.HierarchyCoreModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.HotReloadModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.HotReloadModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ImageConversionModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ImageConversionModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.IMGUIModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputForUIModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.InputForUIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputLegacyModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.InputModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.JSONSerializeModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.JSONSerializeModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.LocalizationModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.LocalizationModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.MarshallingModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.MarshallingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.MultiplayerModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.MultiplayerModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.NVIDIAModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.NVIDIAModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ParticleSystemModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PerformanceReportingModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.PerformanceReportingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.Physics2DModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.Physics2DModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PhysicsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PropertiesModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.PropertiesModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ScreenCaptureModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ScreenCaptureModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ShaderVariantAnalyticsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.ShaderVariantAnalyticsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SharedInternalsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.SharedInternalsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpriteMaskModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.SpriteMaskModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SpriteShapeModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.SpriteShapeModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.StreamingModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.StreamingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SubstanceModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.SubstanceModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.SubsystemsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.SubsystemsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TerrainModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TerrainModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TerrainPhysicsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TerrainPhysicsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextCoreFontEngineModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TextCoreFontEngineModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextCoreTextEngineModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TextCoreTextEngineModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextRenderingModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TilemapModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TilemapModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TLSModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.TLSModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UI">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UIElementsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UIModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UIModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UmbraModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UmbraModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityAnalyticsCommonModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityAnalyticsCommonModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityAnalyticsModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityAnalyticsModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityConnectModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityConnectModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityCurlModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityCurlModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityTestProtocolModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityTestProtocolModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestTextureModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VehiclesModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.VehiclesModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VFXModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.VFXModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VideoModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.VideoModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VirtualTexturingModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.VirtualTexturingModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.VRModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.VRModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.WindModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.WindModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.XRModule">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityEngine.XRModule.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityUIExtensions">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\UnityUIExtensions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="websocket-sharp">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\websocket-sharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Whinarn.UnityMeshSimplifier.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Whinarn.UnityMeshSimplifier.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.AutoLOD">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.AutoLOD.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.ControllerSupport">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.ControllerSupport.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.Core.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.Core.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.JiggleBones">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.JiggleBones.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.PhotonUtility">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.PhotonUtility.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.Settings.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.Settings.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Zorro.UI.Runtime">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\PEAK_Data\Managed\Zorro.UI.Runtime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MelonLoader">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\PEAK\MelonLoader\net35\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="PeakMenu.cs" />
|
||||||
|
<Compile Include="Speed.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
<Exec Command="COPY "$(TargetPath)" "C:\Program Files (x86)\Steam\steamapps\common\PEAK\Mods"" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
20
Peak/PeakMenu.cs
Normal file
20
Peak/PeakMenu.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using MelonLoader;
|
||||||
|
|
||||||
|
[assembly: MelonInfo(typeof(Peak.PeakMenu), "PeakMenu", "1.0.0", "KaseToatz")]
|
||||||
|
[assembly: MelonGame("LandCrab", "PEAK")]
|
||||||
|
|
||||||
|
namespace Peak
|
||||||
|
{
|
||||||
|
public class PeakMenu : MelonMod
|
||||||
|
{
|
||||||
|
public override void OnLateUpdate()
|
||||||
|
{
|
||||||
|
Character local = Character.localCharacter;
|
||||||
|
if (local != null)
|
||||||
|
{
|
||||||
|
local.refs.climbing.climbSpeedMod = 5F;
|
||||||
|
local.data.currentStamina = local.GetMaxStamina();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Peak/Speed.cs
Normal file
16
Peak/Speed.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(CharacterMovement), "GetMovementForce")]
|
||||||
|
public static class Speed
|
||||||
|
{
|
||||||
|
private static readonly FieldInfo character = typeof(CharacterMovement).GetField("character", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
|
||||||
|
public static void Postfix(CharacterMovement __instance, ref float __result)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(character.GetValue(__instance), Character.localCharacter))
|
||||||
|
{
|
||||||
|
__result *= 5F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
PeakMenu.sln
28
PeakMenu.sln
@@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.12.35506.116 d17.12
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PeakMenu", "PeakMenu\PeakMenu.vcxproj", "{AF72AD5F-FAC1-489C-A022-36C08515D36B}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x86.ActiveCfg = Debug|Win32
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x86.Build.0 = Debug|Win32
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x64.Build.0 = Release|x64
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x86.ActiveCfg = Release|Win32
|
|
||||||
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x86.Build.0 = Release|Win32
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<VCProjectVersion>17.0</VCProjectVersion>
|
|
||||||
<Keyword>Win32Proj</Keyword>
|
|
||||||
<ProjectGuid>{af72ad5f-fac1-489c-a022-36c08515d36b}</ProjectGuid>
|
|
||||||
<RootNamespace>PeakMenu</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
|
||||||
<AdditionalIncludeDirectories>C:\Users\raefb\Documents\GitHub\PeakMenu\PeakMenu\g3log;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="g3log\crashhandler_windows.cpp" />
|
|
||||||
<ClCompile Include="g3log\filesink.cpp" />
|
|
||||||
<ClCompile Include="g3log\g3log.cpp" />
|
|
||||||
<ClCompile Include="g3log\logcapture.cpp" />
|
|
||||||
<ClCompile Include="g3log\loglevels.cpp" />
|
|
||||||
<ClCompile Include="g3log\logmessage.cpp" />
|
|
||||||
<ClCompile Include="g3log\logworker.cpp" />
|
|
||||||
<ClCompile Include="g3log\stacktrace_windows.cpp" />
|
|
||||||
<ClCompile Include="g3log\time.cpp" />
|
|
||||||
<ClCompile Include="main.cpp" />
|
|
||||||
<ClCompile Include="pointers.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="common.hpp" />
|
|
||||||
<ClInclude Include="g3log\filesinkhelper.ipp" />
|
|
||||||
<ClInclude Include="g3log\g2log.hpp" />
|
|
||||||
<ClInclude Include="logger.hpp" />
|
|
||||||
<ClInclude Include="memory.hpp" />
|
|
||||||
<ClInclude Include="offsets.hpp" />
|
|
||||||
<ClInclude Include="pointers.hpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="Source Files">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Resource Files">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Source Files\g3log">
|
|
||||||
<UniqueIdentifier>{06b8dc19-35a5-4810-9c1c-bc3255a43a94}</UniqueIdentifier>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="main.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\crashhandler_windows.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\filesink.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\g3log.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\logcapture.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\loglevels.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\logmessage.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\logworker.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\stacktrace_windows.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="g3log\time.cpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="pointers.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="common.hpp">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="g3log\filesinkhelper.ipp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="g3log\g2log.hpp">
|
|
||||||
<Filter>Source Files\g3log</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="logger.hpp">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="memory.hpp">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="pointers.hpp">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="offsets.hpp">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup />
|
|
||||||
</Project>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
namespace peak
|
|
||||||
{
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
inline bool isRunning{true};
|
|
||||||
inline HMODULE hModule;
|
|
||||||
inline HANDLE mainThread{};
|
|
||||||
inline DWORD mainThreadId{};
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
#error "crashhandler_windows.cpp used but not on a windows system"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <process.h> // getpid
|
|
||||||
#include <windows.h>
|
|
||||||
#include <atomic>
|
|
||||||
#include <csignal>
|
|
||||||
#include <cstring>
|
|
||||||
#include <sstream>
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/g3log.hpp"
|
|
||||||
#include "g3log/logcapture.hpp"
|
|
||||||
#include "g3log/stacktrace_windows.hpp"
|
|
||||||
|
|
||||||
#define getpid _getpid
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::atomic<bool> gBlockForFatal{true};
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
|
|
||||||
|
|
||||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
|
||||||
thread_local bool g_installed_thread_signal_handler = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
|
||||||
void* g_vector_exception_handler = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
|
|
||||||
void signalHandler(int signal_number) {
|
|
||||||
using namespace g3::internal;
|
|
||||||
std::string dump = stacktrace::stackdump();
|
|
||||||
|
|
||||||
std::ostringstream fatal_stream;
|
|
||||||
fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
|
|
||||||
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
|
|
||||||
|
|
||||||
LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
|
|
||||||
trigger.stream() << fatal_stream.str();
|
|
||||||
|
|
||||||
// Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens.
|
|
||||||
// Be patient. The "Debug" dialog should pop-up eventually if you doing it in Visual Studio.
|
|
||||||
// For fatal signals only, not exceptions.
|
|
||||||
// This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint
|
|
||||||
// Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger
|
|
||||||
// This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled
|
|
||||||
// ref: g3log/Options.cmake
|
|
||||||
#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
|
|
||||||
__debugbreak();
|
|
||||||
#endif
|
|
||||||
} // scope exit - message sent to LogWorker, wait to die...
|
|
||||||
|
|
||||||
// Unhandled exception catching
|
|
||||||
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) {
|
|
||||||
std::string dump = stacktrace::stackdump(info);
|
|
||||||
|
|
||||||
std::ostringstream fatal_stream;
|
|
||||||
const g3::SignalType exception_code = info->ExceptionRecord->ExceptionCode;
|
|
||||||
fatal_stream << "\n***** " << handler << ": Received fatal exception " << g3::internal::exitReasonName(g3::internal::FATAL_EXCEPTION, exception_code);
|
|
||||||
fatal_stream << "\tPID: " << getpid() << std::endl;
|
|
||||||
|
|
||||||
const auto fatal_id = static_cast<g3::SignalType>(exception_code);
|
|
||||||
LogCapture trigger(g3::internal::FATAL_EXCEPTION, fatal_id, dump.c_str());
|
|
||||||
trigger.stream() << fatal_stream.str();
|
|
||||||
// FATAL Exception: It doesn't necessarily stop here. we pass on continue search
|
|
||||||
// if no one else will catch that then it's goodbye anyhow.
|
|
||||||
// The RISK here is if someone is catching this and returning "EXCEPTION_EXECUTE_HANDLER"
|
|
||||||
// but does not shutdown then the software will be running with g3log shutdown.
|
|
||||||
// .... However... this must be seen as a bug from standard handling of fatal exceptions
|
|
||||||
// https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unhandled exception catching
|
|
||||||
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
|
|
||||||
g3::internal::restoreFatalHandlingToDefault();
|
|
||||||
return exceptionHandling(info, "Unexpected Exception Handler");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Setup through (Windows API) AddVectoredExceptionHandler
|
|
||||||
/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
|
|
||||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
|
||||||
LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
|
|
||||||
const g3::SignalType exception_code = p->ExceptionRecord->ExceptionCode;
|
|
||||||
if (false == stacktrace::isKnownException(exception_code)) {
|
|
||||||
// The unknown exception is ignored. Since it is not a Windows
|
|
||||||
// fatal exception generated by the OS we leave the
|
|
||||||
// responsibility to deal with this by the client software.
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
} else {
|
|
||||||
g3::internal::restoreFatalHandlingToDefault();
|
|
||||||
return exceptionHandling(p, "Vectored Exception Handler");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} // end anonymous namespace
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
// For windows exceptions this might ONCE be set to false, in case of a
|
|
||||||
// windows exceptions and not a signal
|
|
||||||
bool shouldBlockForFatalHandling() {
|
|
||||||
return gBlockForFatal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate stackdump. Or in case a stackdump was pre-generated and
|
|
||||||
/// non-empty just use that one. i.e. the latter case is only for
|
|
||||||
/// Windows and test purposes
|
|
||||||
std::string stackdump(const char* dump) {
|
|
||||||
if (nullptr != dump && !std::string(dump).empty()) {
|
|
||||||
return {dump};
|
|
||||||
}
|
|
||||||
|
|
||||||
return stacktrace::stackdump();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// string representation of signal ID or Windows exception id
|
|
||||||
std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
|
|
||||||
if (level == g3::internal::FATAL_EXCEPTION) {
|
|
||||||
return stacktrace::exceptionIdToText(fatal_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (fatal_id) {
|
|
||||||
case SIGABRT:
|
|
||||||
return "SIGABRT";
|
|
||||||
break;
|
|
||||||
case SIGFPE:
|
|
||||||
return "SIGFPE";
|
|
||||||
break;
|
|
||||||
case SIGSEGV:
|
|
||||||
return "SIGSEGV";
|
|
||||||
break;
|
|
||||||
case SIGILL:
|
|
||||||
return "SIGILL";
|
|
||||||
break;
|
|
||||||
case SIGTERM:
|
|
||||||
return "SIGTERM";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "UNKNOWN SIGNAL(" << fatal_id << ")";
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Triggered by g3log::LogWorker after receiving a FATAL trigger
|
|
||||||
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
|
|
||||||
// --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
|
|
||||||
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType fatal_signal_id) {
|
|
||||||
restoreFatalHandlingToDefault();
|
|
||||||
// For windows exceptions we want to continue the possibility of
|
|
||||||
// exception handling now when the log and stacktrace are flushed
|
|
||||||
// to sinks. We therefore avoid to kill the process here. Instead
|
|
||||||
// it will be the exceptionHandling functions above that
|
|
||||||
// will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH
|
|
||||||
if (g3::internal::FATAL_EXCEPTION == level) {
|
|
||||||
gBlockForFatal = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for a signal however, we exit through that fatal signal
|
|
||||||
const int signal_number = static_cast<int>(fatal_signal_id);
|
|
||||||
raise(signal_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FYI: Concept of async-signal-safe operations does not exist on windows
|
|
||||||
// we stick to perror for lack of better alternatives.
|
|
||||||
size_t writeErrorMessage(const char* message) {
|
|
||||||
perror(message);
|
|
||||||
return std::strlen(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore back to default fatal event handling
|
|
||||||
void restoreFatalHandlingToDefault() {
|
|
||||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
|
||||||
SetUnhandledExceptionFilter(g_previous_unexpected_exception_handler);
|
|
||||||
|
|
||||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
|
||||||
RemoveVectoredExceptionHandler(g_vector_exception_handler);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
|
|
||||||
internal::writeErrorMessage("signal - SIGABRT");
|
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
|
|
||||||
internal::writeErrorMessage("signal - SIGABRT");
|
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
|
|
||||||
internal::writeErrorMessage("signal - SIGABRT");
|
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGILL, SIG_DFL))
|
|
||||||
internal::writeErrorMessage("signal - SIGABRT");
|
|
||||||
|
|
||||||
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
|
|
||||||
internal::writeErrorMessage("signal - SIGABRT");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void installSignalHandler() {
|
|
||||||
g3::installSignalHandlerForThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
|
|
||||||
/// on Windows. This is automatically done if you do at least one LOG(...) call
|
|
||||||
/// you can also use this function call, per thread so make sure these three
|
|
||||||
/// fatal signals are covered in your thread (even if you don't do a LOG(...) call
|
|
||||||
void installSignalHandlerForThread() {
|
|
||||||
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
|
||||||
if (!g_installed_thread_signal_handler) {
|
|
||||||
g_installed_thread_signal_handler = true;
|
|
||||||
if (SIG_ERR == signal(SIGTERM, signalHandler))
|
|
||||||
internal::writeErrorMessage("signal - SIGTERM");
|
|
||||||
if (SIG_ERR == signal(SIGABRT, signalHandler))
|
|
||||||
internal::writeErrorMessage("signal - SIGABRT");
|
|
||||||
if (SIG_ERR == signal(SIGFPE, signalHandler))
|
|
||||||
internal::writeErrorMessage("signal - SIGFPE");
|
|
||||||
if (SIG_ERR == signal(SIGSEGV, signalHandler))
|
|
||||||
internal::writeErrorMessage("signal - SIGSEGV");
|
|
||||||
if (SIG_ERR == signal(SIGILL, signalHandler))
|
|
||||||
internal::writeErrorMessage("signal - SIGILL");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void installCrashHandler() {
|
|
||||||
internal::installSignalHandler();
|
|
||||||
g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling);
|
|
||||||
|
|
||||||
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
|
||||||
// const size_t kFirstExceptionHandler = 1;
|
|
||||||
// kFirstExceptionsHandler is kept here for documentation purposes.
|
|
||||||
// The last exception seems more like what we want.
|
|
||||||
const size_t kLastExceptionHandler = 0;
|
|
||||||
g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace g3
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/filesink.hpp"
|
|
||||||
#include <cassert>
|
|
||||||
#include <chrono>
|
|
||||||
#include "filesinkhelper.ipp"
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
using namespace internal;
|
|
||||||
|
|
||||||
FileSink::FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id, size_t write_to_log_every_x_message) :
|
|
||||||
_log_details_func(&LogMessage::DefaultLogDetailsToString),
|
|
||||||
_log_file_with_path(log_directory),
|
|
||||||
_log_prefix_backup(log_prefix),
|
|
||||||
_outptr(new std::ofstream),
|
|
||||||
_header("\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message\n\n\t\t(uuu*: microseconds fractions of the seconds value)\n\n"),
|
|
||||||
_firstEntry(true),
|
|
||||||
_write_counter(0),
|
|
||||||
_write_to_log_every_x_message(write_to_log_every_x_message) {
|
|
||||||
_log_prefix_backup = prefixSanityFix(log_prefix);
|
|
||||||
if (!isValidFilename(_log_prefix_backup)) {
|
|
||||||
std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
|
|
||||||
_log_file_with_path = pathSanityFix(_log_file_with_path, file_name);
|
|
||||||
_outptr = createLogFile(_log_file_with_path);
|
|
||||||
|
|
||||||
if (!_outptr) {
|
|
||||||
std::cerr << "Cannot write log file to location, attempting current directory" << std::endl;
|
|
||||||
_log_file_with_path = "./" + file_name;
|
|
||||||
_outptr = createLogFile(_log_file_with_path);
|
|
||||||
}
|
|
||||||
assert(_outptr && "cannot open log file at startup");
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSink::~FileSink() {
|
|
||||||
std::string exit_msg = {"g3log g3FileSink shutdown at: "};
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
exit_msg.append(localtime_formatted(now, internal::time_formatted)).append("\n");
|
|
||||||
|
|
||||||
// write anything buffered up and then end with the exit msg
|
|
||||||
filestream() << _write_buffer << exit_msg << std::flush;
|
|
||||||
|
|
||||||
exit_msg.append("Log file at: [").append(_log_file_with_path).append("]\n");
|
|
||||||
std::cerr << exit_msg << std::flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The actual log receiving function
|
|
||||||
void FileSink::fileWrite(LogMessageMover message) {
|
|
||||||
if (_firstEntry) {
|
|
||||||
addLogFileHeader();
|
|
||||||
_firstEntry = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto data = message.get().toString(_log_details_func);
|
|
||||||
|
|
||||||
_write_buffer.append(data);
|
|
||||||
if (++_write_counter % _write_to_log_every_x_message == 0) {
|
|
||||||
filestream() << _write_buffer << std::flush;
|
|
||||||
_write_buffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FileSink::changeLogFile(const std::string& directory, const std::string& logger_id) {
|
|
||||||
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted});
|
|
||||||
|
|
||||||
std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
|
|
||||||
std::string prospect_log = directory + file_name;
|
|
||||||
std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
|
|
||||||
if (nullptr == log_stream) {
|
|
||||||
filestream() << "\n"
|
|
||||||
<< now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
|
|
||||||
return {}; // no success
|
|
||||||
}
|
|
||||||
|
|
||||||
addLogFileHeader();
|
|
||||||
std::ostringstream ss_change;
|
|
||||||
ss_change << "\n\tChanging log file from : " << _log_file_with_path;
|
|
||||||
ss_change << "\n\tto new location: " << prospect_log << "\n";
|
|
||||||
filestream() << now_formatted << ss_change.str();
|
|
||||||
ss_change.str("");
|
|
||||||
|
|
||||||
std::string old_log = _log_file_with_path;
|
|
||||||
_log_file_with_path = std::move(prospect_log);
|
|
||||||
_outptr = std::move(log_stream);
|
|
||||||
ss_change << "\n\tNew log file. The previous log file was at: ";
|
|
||||||
ss_change << old_log << "\n";
|
|
||||||
filestream() << now_formatted << ss_change.str();
|
|
||||||
return _log_file_with_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FileSink::fileName() {
|
|
||||||
return _log_file_with_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSink::overrideLogDetails(LogMessage::LogDetailsFunc func) {
|
|
||||||
_log_details_func = func;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSink::overrideLogHeader(const std::string& change) {
|
|
||||||
_header = change;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSink::addLogFileHeader() {
|
|
||||||
filestream() << header(_header);
|
|
||||||
}
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S";
|
|
||||||
|
|
||||||
// check for filename validity - filename should not be part of PATH
|
|
||||||
bool isValidFilename(const std::string &prefix_filename) {
|
|
||||||
std::string illegal_characters("/,|<>:#$%{}[]\'\"^!?+* ");
|
|
||||||
size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
|
|
||||||
return false;
|
|
||||||
} else if (prefix_filename.empty()) {
|
|
||||||
std::cerr << "Empty filename prefix is not allowed" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string prefixSanityFix(std::string prefix) {
|
|
||||||
prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end());
|
|
||||||
prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end());
|
|
||||||
prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end());
|
|
||||||
prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end());
|
|
||||||
prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end());
|
|
||||||
if (!isValidFilename(prefix)) {
|
|
||||||
return
|
|
||||||
{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string pathSanityFix(std::string path, const std::string &file_name) {
|
|
||||||
// Unify the delimeters,. maybe sketchy solution but it seems to work
|
|
||||||
// on at least win7 + ubuntu. All bets are off for older windows
|
|
||||||
std::replace(path.begin(), path.end(), '\\', '/');
|
|
||||||
|
|
||||||
// clean up in case of multiples
|
|
||||||
auto contains_end = [&](std::string & in) -> bool {
|
|
||||||
size_t size = in.size();
|
|
||||||
if (!size) return false;
|
|
||||||
char end = in[size - 1];
|
|
||||||
return (end == '/' || end == ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
while (contains_end(path)) {
|
|
||||||
path.erase(path.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path.empty()) {
|
|
||||||
path.insert(path.end(), '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
path.insert(path.size(), file_name);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string header(const std::string& headerFormat) {
|
|
||||||
std::ostringstream ss_entry;
|
|
||||||
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n";
|
|
||||||
ss_entry << headerFormat;
|
|
||||||
return ss_entry.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) {
|
|
||||||
std::stringstream oss_name;
|
|
||||||
oss_name << verified_prefix << ".";
|
|
||||||
if( !logger_id.empty() ) {
|
|
||||||
oss_name << logger_id << ".";
|
|
||||||
}
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
oss_name << g3::localtime_formatted(now, file_name_time_formatted);
|
|
||||||
oss_name << ".log";
|
|
||||||
return oss_name.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) {
|
|
||||||
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
|
|
||||||
mode |= std::ios_base::trunc;
|
|
||||||
outstream.open(complete_file_with_path, mode);
|
|
||||||
if (!outstream.is_open()) {
|
|
||||||
std::ostringstream ss_error;
|
|
||||||
ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]";
|
|
||||||
ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
|
|
||||||
std::cerr << ss_error.str().c_str() << std::endl;
|
|
||||||
outstream.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<std::ofstream> createLogFile(const std::string &file_with_full_path) {
|
|
||||||
std::unique_ptr<std::ofstream> out(new std::ofstream);
|
|
||||||
std::ofstream &stream(*(out.get()));
|
|
||||||
bool success_with_open_file = openLogFile(file_with_full_path, stream);
|
|
||||||
if (false == success_with_open_file) {
|
|
||||||
out.reset();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// For convenience: If you don't want to do a recursive search and replace in your source code
|
|
||||||
// for replacing g2log.hpp for g3log/g3log.hpp then you can choose to add this header file to your
|
|
||||||
// code. It will get the necessary includes
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Btw: replacing g2log for g3log include is easy on Linux
|
|
||||||
// find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g'
|
|
||||||
|
|
||||||
#include <g3log/filesink.hpp>
|
|
||||||
#include <g3log/g3log.hpp>
|
|
||||||
#include <g3log/loglevels.hpp>
|
|
||||||
#include <g3log/logworker.hpp>
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
*
|
|
||||||
* Filename:g3log.cpp Framework for Logging and Design By Contract
|
|
||||||
* Created: 2011 by Kjell Hedström
|
|
||||||
*
|
|
||||||
* PUBLIC DOMAIN and Not copyrighted since it was built on public-domain software and at least in "spirit" influenced
|
|
||||||
* from the following sources
|
|
||||||
* 1. kjellkod.cc ;)
|
|
||||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
|
||||||
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
|
||||||
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
|
||||||
* 5. Various Q&A at StackOverflow
|
|
||||||
* ********************************************* */
|
|
||||||
|
|
||||||
#include "g3log/g3log.hpp"
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/loglevels.hpp"
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
#include "g3log/logworker.hpp"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::once_flag g_initialize_flag;
|
|
||||||
g3::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
|
|
||||||
std::mutex g_logging_init_mutex;
|
|
||||||
|
|
||||||
std::unique_ptr<g3::LogMessage> g_first_uninitialized_msg = {nullptr};
|
|
||||||
std::once_flag g_set_first_uninitialized_flag;
|
|
||||||
std::once_flag g_save_first_uninitialized_flag;
|
|
||||||
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */ };
|
|
||||||
std::function<void(void)> g_fatal_pre_logging_hook;
|
|
||||||
|
|
||||||
std::atomic<size_t> g_fatal_hook_recursive_counter = {0};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
// signalhandler and internal clock is only needed to install once
|
|
||||||
// for unit testing purposes the initializeLogging might be called
|
|
||||||
// several times...
|
|
||||||
// for all other practical use, it shouldn't!
|
|
||||||
|
|
||||||
void initializeLogging(LogWorker* bgworker) {
|
|
||||||
std::call_once(g_initialize_flag, [] {
|
|
||||||
installCrashHandler();
|
|
||||||
});
|
|
||||||
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
|
|
||||||
if (internal::isLoggingInitialized() || nullptr == bgworker) {
|
|
||||||
std::ostringstream exitMsg;
|
|
||||||
exitMsg << __FILE__ "->" << __FUNCTION__ << ":" << __LINE__ << std::endl;
|
|
||||||
exitMsg << "\tFatal exit due to illegal initialization of g3::LogWorker\n";
|
|
||||||
exitMsg << "\t(due to multiple initializations? : " << std::boolalpha << internal::isLoggingInitialized();
|
|
||||||
exitMsg << ", due to nullptr == bgworker? : " << std::boolalpha << (nullptr == bgworker) << ")";
|
|
||||||
std::cerr << exitMsg.str() << std::endl;
|
|
||||||
std::exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the first uninitialized message, if any
|
|
||||||
std::call_once(g_save_first_uninitialized_flag, [&bgworker] {
|
|
||||||
if (g_first_uninitialized_msg) {
|
|
||||||
bgworker->save(LogMessagePtr{std::move(g_first_uninitialized_msg)});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
g_logger_instance = bgworker;
|
|
||||||
// by default the pre fatal logging hook does nothing
|
|
||||||
// if it WOULD do something it would happen in
|
|
||||||
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
|
|
||||||
// recursive crash counter re-set to zero
|
|
||||||
g_fatal_hook_recursive_counter.store(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing
|
|
||||||
* It will be called just before sending the fatal message, @ref pushFatalmessageToLogger
|
|
||||||
* It will be reset to do nothing in ::initializeLogging(...)
|
|
||||||
* so please call this function, if you ever need to, after initializeLogging(...)
|
|
||||||
*/
|
|
||||||
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook) {
|
|
||||||
static std::mutex m;
|
|
||||||
std::lock_guard<std::mutex> lock(m);
|
|
||||||
g_fatal_pre_logging_hook = pre_fatal_hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default this function pointer goes to \ref pushFatalMessageToLogger;
|
|
||||||
std::function<void(FatalMessagePtr)> g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger;
|
|
||||||
|
|
||||||
/** REPLACE fatalCallToLogger for fatalCallForUnitTest
|
|
||||||
* This function switches the function pointer so that only
|
|
||||||
* 'unitTest' mock-fatal calls are made.
|
|
||||||
* */
|
|
||||||
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call) {
|
|
||||||
g_fatal_to_g3logworker_function_ptr = fatal_call;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
bool isLoggingInitialized() {
|
|
||||||
return g_logger_instance != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted
|
|
||||||
* that is the responsibility of its owner. *
|
|
||||||
*/
|
|
||||||
void shutDownLogging() {
|
|
||||||
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
|
|
||||||
g_logger_instance = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
|
|
||||||
* LOG(...) calls can happen to a non-existing LogWorker.
|
|
||||||
* @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored
|
|
||||||
* and the logging continues to be active.
|
|
||||||
* @return true if the correct worker was given,. and shutDownLogging was called
|
|
||||||
*/
|
|
||||||
bool shutDownLoggingForActiveOnly(LogWorker* active) {
|
|
||||||
if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
|
|
||||||
LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
|
|
||||||
<< "\n\t\tHaving multiple instances of the g3::LogWorker is likely a BUG"
|
|
||||||
<< "\n\t\tEither way, this call to shutDownLogging was ignored"
|
|
||||||
<< "\n\t\tTry g3::internal::shutDownLogging() instead";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
shutDownLogging();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** explicitly copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
|
|
||||||
* i.e. (dlopen + dlsym) */
|
|
||||||
void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level,
|
|
||||||
const char* boolean_expression, int fatal_signal, const char* stack_trace) {
|
|
||||||
LEVELS msgLevel{level};
|
|
||||||
LogMessagePtr message{std::make_unique<LogMessage>(file, line, function, msgLevel)};
|
|
||||||
message.get()->write().append(entry);
|
|
||||||
message.get()->setExpression(boolean_expression);
|
|
||||||
|
|
||||||
if (internal::wasFatal(level)) {
|
|
||||||
saveFatalMessage(stack_trace, message, fatal_signal);
|
|
||||||
} else {
|
|
||||||
pushMessageToLogger(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal) {
|
|
||||||
auto fatalhook = g_fatal_pre_logging_hook;
|
|
||||||
// In case the fatal_pre logging actually will cause a crash in its turn
|
|
||||||
// let's not do recursive crashing!
|
|
||||||
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
|
|
||||||
++g_fatal_hook_recursive_counter; // thread safe counter
|
|
||||||
// "benign" race here. If two threads crashes, with recursive crashes
|
|
||||||
// then it's possible that the "other" fatal stack trace will be shown
|
|
||||||
// that's OK since it was anyhow the first crash detected
|
|
||||||
static const std::string first_stack_trace = stack_trace;
|
|
||||||
fatalhook();
|
|
||||||
message.get()->write().append(stack_trace);
|
|
||||||
|
|
||||||
if (g_fatal_hook_recursive_counter.load() > 1) {
|
|
||||||
message.get()->write().append(
|
|
||||||
"\n\n\nWARNING\n"
|
|
||||||
"A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n")
|
|
||||||
.append("---First crash stacktrace: ")
|
|
||||||
.append(first_stack_trace)
|
|
||||||
.append("\n---End of first stacktrace\n");
|
|
||||||
}
|
|
||||||
FatalMessagePtr fatal_message{std::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal)};
|
|
||||||
// At destruction, flushes fatal message to g3LogWorker
|
|
||||||
// either we will stay here until the background worker has received the fatal
|
|
||||||
// message, flushed the crash message to the sinks and exits with the same fatal signal
|
|
||||||
//..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
|
|
||||||
fatalCall(fatal_message);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* save the message to the logger. In case of called before the logger is instantiated
|
|
||||||
* the first message will be saved. Any following subsequent uninitialized log calls
|
|
||||||
* will be ignored.
|
|
||||||
*
|
|
||||||
* The first initialized log entry will also save the first uninitialized log message, if any
|
|
||||||
* @param log_entry to save to logger
|
|
||||||
*/
|
|
||||||
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
|
|
||||||
// Uninitialized messages are ignored but does not CHECK/crash the logger
|
|
||||||
if (!internal::isLoggingInitialized()) {
|
|
||||||
std::call_once(g_set_first_uninitialized_flag, [&] {
|
|
||||||
g_first_uninitialized_msg = incoming.release();
|
|
||||||
std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
|
|
||||||
err.append(g_first_uninitialized_msg->message());
|
|
||||||
std::string& str = g_first_uninitialized_msg->write();
|
|
||||||
str.clear();
|
|
||||||
str.append(err); // replace content
|
|
||||||
std::cerr << str << std::endl; });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// logger is initialized
|
|
||||||
g_logger_instance->save(incoming);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
|
|
||||||
* to exit the program. After saving the fatal message the calling thread
|
|
||||||
* will sleep forever (i.e. until the background thread catches up, saves the fatal
|
|
||||||
* message and kills the software with the fatal signal.
|
|
||||||
*/
|
|
||||||
void pushFatalMessageToLogger(FatalMessagePtr message) {
|
|
||||||
if (!isLoggingInitialized()) {
|
|
||||||
std::ostringstream error;
|
|
||||||
error << "FATAL CALL but logger is NOT initialized\n"
|
|
||||||
<< "CAUSE: " << message.get()->reason()
|
|
||||||
<< "\nMessage: \n"
|
|
||||||
<< message.get()->toString() << std::flush;
|
|
||||||
std::cerr << error.str() << std::flush;
|
|
||||||
internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
|
|
||||||
}
|
|
||||||
g_logger_instance->fatal(message);
|
|
||||||
while (shouldBlockForFatalHandling()) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The default, initial, handling to send a 'fatal' event to g3logworker
|
|
||||||
* the caller will stay here, eternally, until the software is aborted
|
|
||||||
* ... in the case of unit testing it is the given "Mock" fatalCall that will
|
|
||||||
* define the behaviour.
|
|
||||||
*/
|
|
||||||
void fatalCall(FatalMessagePtr message) {
|
|
||||||
g_fatal_to_g3logworker_function_ptr(FatalMessagePtr{std::move(message)});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
*
|
|
||||||
* Example of a Active Object, using C++11 std::thread mechanisms to make it
|
|
||||||
* safe for thread communication.
|
|
||||||
*
|
|
||||||
* This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x
|
|
||||||
* and inspired from Herb Sutter's C++11 Active Object
|
|
||||||
* http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads
|
|
||||||
*
|
|
||||||
* Last update 2013-12-19 by Kjell Hedstrom,
|
|
||||||
* e-mail: hedstrom at kjellkod dot cc
|
|
||||||
* linkedin: http://linkedin.com/se/kjellkod */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include "g3log/shared_queue.hpp"
|
|
||||||
|
|
||||||
namespace kjellkod {
|
|
||||||
typedef std::function<void()> Callback;
|
|
||||||
|
|
||||||
class Active {
|
|
||||||
private:
|
|
||||||
Active() :
|
|
||||||
done_(false) {} // Construction ONLY through factory createActive();
|
|
||||||
Active(const Active&) = delete;
|
|
||||||
Active& operator=(const Active&) = delete;
|
|
||||||
|
|
||||||
void run() {
|
|
||||||
while (!done_) {
|
|
||||||
Callback func;
|
|
||||||
mq_.wait_and_pop(func);
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_queue<Callback> mq_;
|
|
||||||
std::thread thd_;
|
|
||||||
bool done_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~Active() {
|
|
||||||
send([this]() noexcept { done_ = true; });
|
|
||||||
thd_.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void send(Callback msg_) {
|
|
||||||
mq_.push(msg_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Factory: safe construction of object before thread start
|
|
||||||
static std::unique_ptr<Active> createActive() {
|
|
||||||
std::unique_ptr<Active> aPtr(new Active());
|
|
||||||
aPtr->thd_ = std::thread(&Active::run, aPtr.get());
|
|
||||||
return aPtr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace kjellkod
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
/// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c
|
|
||||||
struct atomicbool {
|
|
||||||
private:
|
|
||||||
std::atomic<bool> value_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
atomicbool() :
|
|
||||||
value_{false} {}
|
|
||||||
atomicbool(bool value) :
|
|
||||||
value_{value} {}
|
|
||||||
atomicbool(const std::atomic<bool>& value) :
|
|
||||||
value_{value.load(std::memory_order_acquire)} {}
|
|
||||||
atomicbool(const atomicbool& other) :
|
|
||||||
value_{other.value_.load(std::memory_order_acquire)} {}
|
|
||||||
|
|
||||||
atomicbool& operator=(const atomicbool& other) {
|
|
||||||
value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
atomicbool& operator=(const bool other) {
|
|
||||||
value_.store(other, std::memory_order_release);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const atomicbool& rhs) const {
|
|
||||||
return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool value() { return value_.load(std::memory_order_acquire); }
|
|
||||||
std::atomic<bool>& get() { return value_; }
|
|
||||||
};
|
|
||||||
} // namespace g3
|
|
||||||
// explicit whitespace/EOF for VS15
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/** ==========================================================================
|
|
||||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
#include <cstdio>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include "g3log/loglevels.hpp"
|
|
||||||
|
|
||||||
// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
|
|
||||||
// implementationsfilen kan vara den samma
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
// PUBLIC API:
|
|
||||||
/** Install signal handler that catches FATAL C-runtime or OS signals
|
|
||||||
See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE
|
|
||||||
See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling
|
|
||||||
SIGABRT ABORT (ANSI), abnormal termination
|
|
||||||
SIGFPE Floating point exception (ANSI)
|
|
||||||
SIGILL ILlegal instruction (ANSI)
|
|
||||||
SIGSEGV Segmentation violation i.e. illegal memory reference
|
|
||||||
SIGTERM TERMINATION (ANSI) */
|
|
||||||
void installCrashHandler();
|
|
||||||
|
|
||||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
typedef unsigned long SignalType;
|
|
||||||
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
|
|
||||||
/// on Windows. This is automatically done if you do at least one LOG(...) call
|
|
||||||
/// you can also use this function call, per thread so make sure these three
|
|
||||||
/// fatal signals are covered in your thread (even if you don't do a LOG(...) call
|
|
||||||
void installSignalHandlerForThread();
|
|
||||||
#else
|
|
||||||
typedef int SignalType;
|
|
||||||
std::string signalToStr(int signal_number);
|
|
||||||
|
|
||||||
// restore to whatever signal handler was used before signal handler installation
|
|
||||||
void restoreSignalHandler(int signal_number);
|
|
||||||
|
|
||||||
/// Overrides the existing signal handling for custom signals
|
|
||||||
/// For example: usage of zcmq relies on its own signal handler for SIGTERM
|
|
||||||
/// so users of g3log with zcmq should then use the @ref overrideSetupSignals
|
|
||||||
/// , likely with the original set of signals but with SIGTERM removed
|
|
||||||
///
|
|
||||||
/// call example:
|
|
||||||
/// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"},
|
|
||||||
// {SIGSEGV, "SIGSEGV"},});
|
|
||||||
void overrideSetupSignals(const std::map<int, std::string> overrideSignals);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
/// Resets the fatal signal/exception handling back to default
|
|
||||||
/// which might be needed in case it was previously overridden
|
|
||||||
/// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
|
|
||||||
void restoreFatalHandlingToDefault();
|
|
||||||
|
|
||||||
/** return whether or any fatal handling is still ongoing
|
|
||||||
* this is used by g3log::fatalCallToLogger
|
|
||||||
* only in the case of Windows exceptions (not fatal signals)
|
|
||||||
* are we interested in changing this from false to true to
|
|
||||||
* help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/
|
|
||||||
bool shouldBlockForFatalHandling();
|
|
||||||
|
|
||||||
/** \return signal_name Ref: signum.hpp and \ref installSignalHandler
|
|
||||||
* or for Windows exception name */
|
|
||||||
std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number);
|
|
||||||
|
|
||||||
/** return calling thread's stackdump*/
|
|
||||||
std::string stackdump(const char* dump = nullptr);
|
|
||||||
|
|
||||||
/** Re-"throw" a fatal signal, previously caught. This will exit the application
|
|
||||||
* This is an internal only function. Do not use it elsewhere. It is triggered
|
|
||||||
* from g3log, g3LogWorker after flushing messages to file */
|
|
||||||
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
|
|
||||||
size_t writeErrorMessage(const char* message);
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
class FileSink {
|
|
||||||
public:
|
|
||||||
FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id = "g3log", size_t write_to_log_every_x_message = 100);
|
|
||||||
virtual ~FileSink();
|
|
||||||
|
|
||||||
void fileWrite(LogMessageMover message);
|
|
||||||
std::string changeLogFile(const std::string& directory, const std::string& logger_id);
|
|
||||||
std::string fileName();
|
|
||||||
void overrideLogDetails(LogMessage::LogDetailsFunc func);
|
|
||||||
void overrideLogHeader(const std::string& change);
|
|
||||||
|
|
||||||
private:
|
|
||||||
LogMessage::LogDetailsFunc _log_details_func;
|
|
||||||
std::string _log_file_with_path;
|
|
||||||
std::string _log_prefix_backup; // needed in case of future log file changes of directory
|
|
||||||
std::unique_ptr<std::ofstream> _outptr;
|
|
||||||
std::string _header;
|
|
||||||
bool _firstEntry;
|
|
||||||
std::string _write_buffer;
|
|
||||||
size_t _write_counter;
|
|
||||||
size_t _write_to_log_every_x_message;
|
|
||||||
|
|
||||||
void addLogFileHeader();
|
|
||||||
std::ofstream& filestream() {
|
|
||||||
return *(_outptr.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSink& operator=(const FileSink&) = delete;
|
|
||||||
FileSink(const FileSink& other) = delete;
|
|
||||||
};
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
* Filename:g3future.hpp
|
|
||||||
* Helper functionality to put packaged_tasks in standard container. This
|
|
||||||
* is especially helpful for background thread processing a la async but through
|
|
||||||
* an actor pattern (active object), thread pool or similar.
|
|
||||||
* Created: 2012 by Kjell Hedström
|
|
||||||
*
|
|
||||||
* COMMUNITY THANKS:
|
|
||||||
* The code below is in large thanks to exemplifying code snippets from StackOverflow
|
|
||||||
* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm
|
|
||||||
* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
|
|
||||||
*
|
|
||||||
* Both are highly recommended reads if you are interested in c++ concurrency library
|
|
||||||
* - Kjell, 2012
|
|
||||||
*
|
|
||||||
* PUBLIC DOMAIN and NOT under copywrite protection.
|
|
||||||
* ********************************************* */
|
|
||||||
|
|
||||||
#include <future>
|
|
||||||
#include "g3log/active.hpp"
|
|
||||||
#include "g3log/moveoncopy.hpp"
|
|
||||||
#include "g3log/stlpatch_future.hpp"
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
// Generic helper function to avoid repeating the steps for managing
|
|
||||||
// asynchronous task job (by active object) that returns a future results
|
|
||||||
// could of course be made even more generic if done more in the way of
|
|
||||||
// std::async, ref: http://en.cppreference.com/w/cpp/thread/async
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
// std::unique_ptr<Active> bgWorker{Active::createActive()};
|
|
||||||
// ...
|
|
||||||
// auto msg_call=[=](){return ("Hello from the Background");};
|
|
||||||
// auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get());
|
|
||||||
template <typename Func, class BgWorker>
|
|
||||||
std::future<std::invoke_result_t<Func>> spawn_task(Func func, BgWorker* worker) {
|
|
||||||
typedef std::invoke_result_t<Func> result_type;
|
|
||||||
typedef std::packaged_task<result_type()> task_type;
|
|
||||||
|
|
||||||
if (nullptr == worker) {
|
|
||||||
auto p = std::make_shared<std::promise<result_type>>();
|
|
||||||
std::future<result_type> future_result = p->get_future();
|
|
||||||
p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker")));
|
|
||||||
return future_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
task_type task(std::move(func));
|
|
||||||
|
|
||||||
std::future<result_type> result = task.get_future();
|
|
||||||
worker->send(MoveOnCopy<task_type>(std::move(task)));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} // end namespace g3
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
*
|
|
||||||
* Filename:g3log.hpp Framework for Logging and Design By Contract
|
|
||||||
* Created: 2011 by Kjell Hedström
|
|
||||||
*
|
|
||||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
|
||||||
* at least in "spirit" from the following sources
|
|
||||||
* 1. kjellkod.cc ;)
|
|
||||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/caddpp/
|
|
||||||
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
|
||||||
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
|
||||||
* 5. Various Q&A at StackOverflow
|
|
||||||
* ********************************************* */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "g3log/logcapture.hpp"
|
|
||||||
#include "g3log/loglevels.hpp"
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) && (defined(WINDOWS_FUNCSIG)) // Microsoft
|
|
||||||
#define G3LOG_PRETTY_FUNCTION __FUNCSIG__
|
|
||||||
#elif defined(__GNUC__) && defined(PRETTY_FUNCTION) // GCC compatible
|
|
||||||
#define G3LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
|
||||||
#else
|
|
||||||
#define G3LOG_PRETTY_FUNCTION __FUNCTION__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// thread_local doesn't exist before VS2013
|
|
||||||
// it exists on VS2015
|
|
||||||
#if !(defined(thread_local)) && defined(_MSC_VER) && _MSC_VER < 1900
|
|
||||||
#define thread_local __declspec(thread)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** namespace for LOG() and CHECK() frameworks
|
|
||||||
* History lesson: Why the names 'g3' and 'g3log'?:
|
|
||||||
* The framework was made in my own free time as PUBLIC DOMAIN but the
|
|
||||||
* first commercial project to use it used 'g3' as an internal denominator for
|
|
||||||
* the current project. g3 as in 'generation 2'. I decided to keep the g3 and g3log names
|
|
||||||
* to give credit to the people in that project (you know who you are :) and I guess also
|
|
||||||
* for 'sentimental' reasons. That a big influence was Google's glog is just a happy
|
|
||||||
* coincidence or subconscious choice. Either way g3log became the name for this logger.
|
|
||||||
*
|
|
||||||
* --- Thanks for a great 2011 and good luck with 'g3' --- KjellKod
|
|
||||||
*/
|
|
||||||
namespace g3 {
|
|
||||||
class LogWorker;
|
|
||||||
struct LogMessage;
|
|
||||||
struct FatalMessage;
|
|
||||||
|
|
||||||
/** Should be called at very first startup of the software with \ref g3LogWorker
|
|
||||||
* pointer. Ownership of the \ref g3LogWorker is the responsibility of the caller */
|
|
||||||
void initializeLogging(LogWorker* logger);
|
|
||||||
|
|
||||||
/** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called
|
|
||||||
*
|
|
||||||
* Set a function-hook before a fatal message will be sent to the logger
|
|
||||||
* i.e. this is a great place to put a break point, either in your debugger
|
|
||||||
* or programmatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal)
|
|
||||||
* This will be reset to default (does nothing) at initializeLogging(...);
|
|
||||||
*
|
|
||||||
* Example usage:
|
|
||||||
* Windows: g3::setFatalPreLoggingHook([]{__debugbreak();}); // remember #include <intrin.h>
|
|
||||||
* WARNING: '__debugbreak()' when not running in Debug in your Visual Studio IDE will likely
|
|
||||||
* trigger a recursive crash if used here. It should only be used when debugging
|
|
||||||
* in your Visual Studio IDE. Recursive crashes are handled but are unnecessary.
|
|
||||||
*
|
|
||||||
* Linux: g3::setFatalPreLoggingHook([]{ raise(SIGTRAP); });
|
|
||||||
*/
|
|
||||||
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook);
|
|
||||||
|
|
||||||
/** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then
|
|
||||||
* use "setFatalExithandler". Please see g3log.cpp and crashhandler_windows.cpp or crashhandler_unix for
|
|
||||||
* example of restoring signal and exception handlers, flushing the log and shutting down.
|
|
||||||
*/
|
|
||||||
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call);
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
|
||||||
// only_change_at_initialization namespace is for changes to be done only during initialization. More specifically
|
|
||||||
// items here would be called prior to calling other parts of g3log
|
|
||||||
namespace only_change_at_initialization {
|
|
||||||
// Sets the MaxMessageSize to be used when capturing log messages. Currently this value is set to 2KB. Messages
|
|
||||||
// Longer than this are bound to 2KB with the string "[...truncated...]" at the end. This function allows
|
|
||||||
// this limit to be changed.
|
|
||||||
void setMaxMessageSize(size_t max_size);
|
|
||||||
} // namespace only_change_at_initialization
|
|
||||||
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
|
|
||||||
|
|
||||||
// internal namespace is for completely internal or semi-hidden from the g3 namespace due to that it is unlikely
|
|
||||||
// that you will use these
|
|
||||||
namespace internal {
|
|
||||||
/// @returns true if logger is initialized
|
|
||||||
bool isLoggingInitialized();
|
|
||||||
|
|
||||||
// Save the created LogMessage to any existing sinks
|
|
||||||
void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level,
|
|
||||||
const char* boolean_expression, int fatal_signal, const char* stack_trace);
|
|
||||||
|
|
||||||
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal);
|
|
||||||
|
|
||||||
// forwards the message to all sinks
|
|
||||||
void pushMessageToLogger(LogMessagePtr log_entry);
|
|
||||||
|
|
||||||
// forwards a FATAL message to all sinks,. after which the g3logworker
|
|
||||||
// will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler
|
|
||||||
//
|
|
||||||
// By default the "fatalCall" will forward a FatalMessageptr to this function
|
|
||||||
// this behavior can be changed if you set a different fatal handler through
|
|
||||||
// "setFatalExitHandler"
|
|
||||||
void pushFatalMessageToLogger(FatalMessagePtr message);
|
|
||||||
|
|
||||||
// Saves the created FatalMessage to any existing sinks and exits with
|
|
||||||
// the originating fatal signal,. or SIGABRT if it originated from a broken contract.
|
|
||||||
// By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override
|
|
||||||
//
|
|
||||||
// If you override it then you probably want to call "pushFatalMessageToLogger" after your
|
|
||||||
// custom fatal handler is done. This will make sure that the fatal message the pushed
|
|
||||||
// to sinks as well as shutting down the process
|
|
||||||
void fatalCall(FatalMessagePtr message);
|
|
||||||
|
|
||||||
// Shuts down logging. No object cleanup but further LOG(...) calls will be ignored.
|
|
||||||
void shutDownLogging();
|
|
||||||
|
|
||||||
// Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized
|
|
||||||
bool shutDownLoggingForActiveOnly(LogWorker* active);
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace g3
|
|
||||||
// clang-format off
|
|
||||||
#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, static_cast<const char*>(G3LOG_PRETTY_FUNCTION), level)
|
|
||||||
|
|
||||||
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
|
|
||||||
LogCapture(__FILE__, __LINE__, G3LOG_PRETTY_FUNCTION, g3::internal::CONTRACT, boolean_expression)
|
|
||||||
|
|
||||||
|
|
||||||
// LOG(level) is the API for the stream log
|
|
||||||
#define LOG(level) if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).stream()
|
|
||||||
|
|
||||||
|
|
||||||
// 'Conditional' stream log
|
|
||||||
#define LOG_IF(level, boolean_expression) \
|
|
||||||
if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).stream()
|
|
||||||
|
|
||||||
// 'Design By Contract' stream API. Broken Contracts will exit the application by using fatal signal SIGABRT
|
|
||||||
// For unit testing, you can override the fatal handling using setFatalExitHandler(...). See tes_io.cpp for examples
|
|
||||||
#define CHECK(boolean_expression) \
|
|
||||||
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
|
|
||||||
|
|
||||||
|
|
||||||
/** For details please see this
|
|
||||||
* REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format
|
|
||||||
* \verbatim
|
|
||||||
*
|
|
||||||
There are different %-codes for different variable types, as well as options to
|
|
||||||
limit the length of the variables and whatnot.
|
|
||||||
Code Format
|
|
||||||
%[flags][width][.precision][length]specifier
|
|
||||||
SPECIFIERS
|
|
||||||
----------
|
|
||||||
%c character
|
|
||||||
%d signed integers
|
|
||||||
%i signed integers
|
|
||||||
%e scientific notation, with a lowercase “e”
|
|
||||||
%E scientific notation, with a uppercase “E”
|
|
||||||
%f floating point
|
|
||||||
%g use %e or %f, whichever is shorter
|
|
||||||
%G use %E or %f, whichever is shorter
|
|
||||||
%o octal
|
|
||||||
%s a string of characters
|
|
||||||
%u unsigned integer
|
|
||||||
%x unsigned hexadecimal, with lowercase letters
|
|
||||||
%X unsigned hexadecimal, with uppercase letters
|
|
||||||
%p a pointer
|
|
||||||
%n the argument shall be a pointer to an integer into which is placed the number of characters written so far
|
|
||||||
|
|
||||||
For flags, width, precision etc please see the above references.
|
|
||||||
EXAMPLES:
|
|
||||||
{
|
|
||||||
LOGF(INFO, "Characters: %c %c \n", 'a', 65);
|
|
||||||
LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long
|
|
||||||
LOGF(INFO, "Preceding with blanks: %10d \n", 1977);
|
|
||||||
LOGF(INFO, "Preceding with zeros: %010d \n", 1977);
|
|
||||||
LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
|
|
||||||
LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
|
|
||||||
LOGF(INFO, "Width trick: %*d \n", 5, 10);
|
|
||||||
LOGF(INFO, "%s \n", "A string");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
And here is possible output
|
|
||||||
: Characters: a A
|
|
||||||
: Decimals: 1977 650000
|
|
||||||
: Preceding with blanks: 1977
|
|
||||||
: Preceding with zeros: 0000001977
|
|
||||||
: Some different radixes: 100 64 144 0x64 0144
|
|
||||||
: floats: 3.14 +3e+000 3.141600E+000
|
|
||||||
: Width trick: 10
|
|
||||||
: A string \endverbatim */
|
|
||||||
#define LOGF(level, printf_like_message, ...) \
|
|
||||||
if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
// Conditional log printf syntax
|
|
||||||
#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
|
|
||||||
if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
// Design By Contract, printf-like API syntax with variadic input parameters.
|
|
||||||
// Calls the signal handler if the contract failed with the default exit for a failed contract. This is typically SIGABRT
|
|
||||||
// See g3log, setFatalExitHandler(...) which can be overriden for unit tests (ref test_io.cpp)
|
|
||||||
#define CHECKF(boolean_expression, printf_like_message, ...) \
|
|
||||||
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
// Backwards compatible. The same as CHECKF.
|
|
||||||
// Design By Contract, printf-like API syntax with variadic input parameters.
|
|
||||||
// Calls the signal handler if the contract failed. See g3log, setFatalExitHandler(...) which can be overriden for unit tests
|
|
||||||
// (ref test_io.cpp)
|
|
||||||
#define CHECK_F(boolean_expression, printf_like_message, ...) \
|
|
||||||
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
|
|
||||||
// clang-format on
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/loglevels.hpp"
|
|
||||||
|
|
||||||
#include <csignal>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <sal.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple struct for capturing log/fatal entries. At destruction the captured message is
|
|
||||||
* forwarded to background worker.
|
|
||||||
* As a safety precaution: No memory allocated here will be moved into the background
|
|
||||||
* worker in case of dynamic loaded library reasons
|
|
||||||
*/
|
|
||||||
struct LogCapture {
|
|
||||||
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
|
|
||||||
LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file, line, function are given in g3log.hpp from macros
|
|
||||||
* @level INFO/DEBUG/WARNING/FATAL
|
|
||||||
* @expression for CHECK calls
|
|
||||||
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
|
|
||||||
*/
|
|
||||||
LogCapture(const char* file, const int line, const char* function, const LEVELS& level, const char* expression = "", g3::SignalType fatal_signal = SIGABRT, const char* dump = nullptr);
|
|
||||||
|
|
||||||
// At destruction the message will be forwarded to the g3log worker.
|
|
||||||
// In the case of dynamically (at runtime) loaded libraries, the important thing to know is that
|
|
||||||
// all strings are copied, so the original are not destroyed at the receiving end, only the copy
|
|
||||||
virtual ~LogCapture() noexcept(false);
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#if _MSC_VER >= 1400
|
|
||||||
#define G3LOG_FORMAT_STRING _Printf_format_string_
|
|
||||||
#else
|
|
||||||
#define G3LOG_FORMAT_STRING __format_string
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...);
|
|
||||||
#else
|
|
||||||
#define G3LOG_FORMAT_STRING
|
|
||||||
|
|
||||||
// Use "-Wall" to generate warnings in case of illegal printf format.
|
|
||||||
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
|
|
||||||
[[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// prettifying API for this completely open struct
|
|
||||||
std::ostringstream& stream() {
|
|
||||||
return _stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream _stream;
|
|
||||||
std::string _stack_trace;
|
|
||||||
const char* _file;
|
|
||||||
const int _line;
|
|
||||||
const char* _function;
|
|
||||||
const LEVELS& _level;
|
|
||||||
const char* _expression;
|
|
||||||
const g3::SignalType _fatal_signal;
|
|
||||||
};
|
|
||||||
//} // g3
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Users of Juce or other libraries might have a define DEBUG which clashes with
|
|
||||||
// the DEBUG logging level for G3log. In that case they can instead use the define
|
|
||||||
// "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
|
|
||||||
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
|
|
||||||
#if (defined(DBUG))
|
|
||||||
#error "DBUG is already defined elsewhere which clashes with G3Log's log level DBUG"
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#if (defined(DEBUG))
|
|
||||||
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <atomic>
|
|
||||||
#include <g3log/atomicbool.hpp>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
|
|
||||||
struct LEVELS {
|
|
||||||
// force internal copy of the const char*. This is a simple safeguard for when g3log is used in a
|
|
||||||
// "dynamic, runtime loading of shared libraries"
|
|
||||||
|
|
||||||
LEVELS(const LEVELS& other) :
|
|
||||||
value(other.value),
|
|
||||||
text(other.text.c_str()) {}
|
|
||||||
|
|
||||||
LEVELS(int id, const std::string& idtext) :
|
|
||||||
value(id),
|
|
||||||
text(idtext) {}
|
|
||||||
|
|
||||||
bool operator==(const LEVELS& rhs) const {
|
|
||||||
return (value == rhs.value && text == rhs.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const LEVELS& rhs) const {
|
|
||||||
return (value != rhs.value || text != rhs.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend void swap(LEVELS& first, LEVELS& second) {
|
|
||||||
using std::swap;
|
|
||||||
swap(first.value, second.value);
|
|
||||||
swap(first.text, second.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
LEVELS& operator=(LEVELS other) {
|
|
||||||
swap(*this, other);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
int value;
|
|
||||||
std::string text;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If you want to add any extra logging level then please add to your own source file the logging level you need
|
|
||||||
// 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...).
|
|
||||||
// to give g3log a record of your logging level and if it is an enabled or disbled logging level.
|
|
||||||
//
|
|
||||||
// 2. If the cmake dynamic logging option is turned OFF
|
|
||||||
// then giving g3log a record of your logging level with 'addLogLevel(...) is NOT needed since no "disbled/enabled"
|
|
||||||
// check will happen - all logging levels will be considered enabled.
|
|
||||||
// 3. See also the [g3log/API.markdown](https://github.com/KjellKod/g3log/blob/master/API.markdown) for for information.
|
|
||||||
//
|
|
||||||
// example: MyLoggingLevel.h
|
|
||||||
// #pragma once
|
|
||||||
// const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"};
|
|
||||||
// const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"};
|
|
||||||
//
|
|
||||||
// ... somewhere else when G3_DYNAMIC_LOGGING is enabled
|
|
||||||
// addLogLevel(MYINFO, true);
|
|
||||||
// LOG(MYINFO) << "some text";
|
|
||||||
//
|
|
||||||
// ... another example, when G3_DYNAMIC_LOGGING is enabled
|
|
||||||
// 'addLogLevel' is NOT required
|
|
||||||
// LOG(MYFATAL) << "this will just work, and it will be counted as a FATAL event";
|
|
||||||
namespace g3 {
|
|
||||||
static const int kDebugValue = 100;
|
|
||||||
static const int kInfoValue = 300;
|
|
||||||
static const int kWarningValue = 500;
|
|
||||||
static const int kFatalValue = 1000;
|
|
||||||
static const int kInternalFatalValue = 2000;
|
|
||||||
} // namespace g3
|
|
||||||
|
|
||||||
const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"},
|
|
||||||
INFO{g3::kInfoValue, "INFO"},
|
|
||||||
WARNING{g3::kWarningValue, "WARNING"},
|
|
||||||
FATAL{g3::kFatalValue, "FATAL"};
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
// Logging level and atomic status collection struct
|
|
||||||
struct LoggingLevel {
|
|
||||||
atomicbool status;
|
|
||||||
LEVELS level;
|
|
||||||
|
|
||||||
// default operator needed for std::map compliance
|
|
||||||
LoggingLevel() :
|
|
||||||
status(false),
|
|
||||||
level(INFO){};
|
|
||||||
LoggingLevel(const LoggingLevel& lvl) :
|
|
||||||
status(lvl.status),
|
|
||||||
level(lvl.level) {}
|
|
||||||
LoggingLevel(const LEVELS& lvl) :
|
|
||||||
status(true),
|
|
||||||
level(lvl){};
|
|
||||||
LoggingLevel(const LEVELS& lvl, bool enabled) :
|
|
||||||
status(enabled),
|
|
||||||
level(lvl){};
|
|
||||||
~LoggingLevel() = default;
|
|
||||||
|
|
||||||
LoggingLevel& operator=(const LoggingLevel& other) {
|
|
||||||
status = other.status;
|
|
||||||
level = other.level;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const LoggingLevel& rhs) const {
|
|
||||||
return (status == rhs.status && level == rhs.level);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace g3
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
const LEVELS CONTRACT{g3::kInternalFatalValue, {"CONTRACT"}},
|
|
||||||
FATAL_SIGNAL{g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}},
|
|
||||||
FATAL_EXCEPTION{kInternalFatalValue + 2, {"FATAL_EXCEPTION"}};
|
|
||||||
|
|
||||||
/// helper function to tell the logger if a log message was fatal. If it is it will force
|
|
||||||
/// a shutdown after all log entries are saved to the sinks
|
|
||||||
bool wasFatal(const LEVELS& level);
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_LOGGING
|
|
||||||
// Only safe if done at initialization in a single-thread context
|
|
||||||
namespace only_change_at_initialization {
|
|
||||||
|
|
||||||
/// add a custom level - enabled or disabled
|
|
||||||
void addLogLevel(LEVELS level, bool enabled);
|
|
||||||
|
|
||||||
/// add a custom level - enabled
|
|
||||||
void addLogLevel(LEVELS level);
|
|
||||||
|
|
||||||
/// reset all default logging levels to enabled
|
|
||||||
/// remove any added logging levels so that the only ones left are
|
|
||||||
/// {DEBUG,INFO,WARNING,FATAL}
|
|
||||||
void reset();
|
|
||||||
} // namespace only_change_at_initialization
|
|
||||||
|
|
||||||
namespace log_levels {
|
|
||||||
/// Enable log level >= log_level.
|
|
||||||
/// log levels below will be disabled
|
|
||||||
/// log levels equal or higher will be enabled.
|
|
||||||
void setHighest(LEVELS level);
|
|
||||||
|
|
||||||
void set(LEVELS level, bool enabled);
|
|
||||||
void disable(LEVELS level);
|
|
||||||
void enable(LEVELS level);
|
|
||||||
|
|
||||||
/// WARNING: This will also disable FATAL events from being logged
|
|
||||||
void disableAll();
|
|
||||||
void enableAll();
|
|
||||||
|
|
||||||
/// print all levels with their disabled or enabled status
|
|
||||||
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint);
|
|
||||||
|
|
||||||
/// print snapshot of system levels with their
|
|
||||||
/// disabled or enabled status
|
|
||||||
std::string to_string();
|
|
||||||
|
|
||||||
/// Snapshot view of the current logging levels' status
|
|
||||||
std::map<int, g3::LoggingLevel> getAll();
|
|
||||||
|
|
||||||
enum class status { Absent,
|
|
||||||
Enabled,
|
|
||||||
Disabled };
|
|
||||||
status getStatus(LEVELS level);
|
|
||||||
} // namespace log_levels
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/// Enabled status for the given logging level
|
|
||||||
bool logLevel(const LEVELS& level);
|
|
||||||
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/loglevels.hpp"
|
|
||||||
#include "g3log/moveoncopy.hpp"
|
|
||||||
#include "g3log/time.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
/** LogMessage contains all the data collected from the LOG(...) call.
|
|
||||||
* If the sink receives a std::string it will be the std::string toString()... function
|
|
||||||
* that will format the data into a string
|
|
||||||
*
|
|
||||||
* For sinks that receive a LogMessage they can either use the toString() function, or use
|
|
||||||
* the helper functions or even the public raw data to format the saved log message any
|
|
||||||
* desired way.
|
|
||||||
*/
|
|
||||||
struct LogMessage {
|
|
||||||
std::string file_path() const {
|
|
||||||
return _file_path;
|
|
||||||
}
|
|
||||||
std::string file() const {
|
|
||||||
return _file;
|
|
||||||
}
|
|
||||||
std::string line() const {
|
|
||||||
return std::to_string(_line);
|
|
||||||
}
|
|
||||||
std::string function() const {
|
|
||||||
return _function;
|
|
||||||
}
|
|
||||||
std::string level() const {
|
|
||||||
return _level.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// use a different format string to get a different look on the time.
|
|
||||||
// default look is Y/M/D H:M:S
|
|
||||||
std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
|
|
||||||
|
|
||||||
std::string message() const {
|
|
||||||
return _message;
|
|
||||||
}
|
|
||||||
std::string& write() const {
|
|
||||||
return _message;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string expression() const {
|
|
||||||
return _expression;
|
|
||||||
}
|
|
||||||
bool wasFatal() const {
|
|
||||||
return internal::wasFatal(_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string threadID() const;
|
|
||||||
|
|
||||||
void setExpression(std::string expression) {
|
|
||||||
_expression = std::move(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage& operator=(LogMessage other);
|
|
||||||
|
|
||||||
LogMessage(std::string file, const int line, std::string function, const LEVELS level);
|
|
||||||
|
|
||||||
explicit LogMessage(const std::string& fatalOsSignalCrashMessage);
|
|
||||||
LogMessage(const LogMessage& other);
|
|
||||||
LogMessage(LogMessage&& other);
|
|
||||||
virtual ~LogMessage() {}
|
|
||||||
|
|
||||||
// helper log printing functions used by "toString()"
|
|
||||||
static std::string splitFileName(const std::string& str);
|
|
||||||
static std::string fatalSignalToString(const LogMessage& msg);
|
|
||||||
// windows only: fatalExceptionToString
|
|
||||||
static std::string fatalExceptionToString(const LogMessage& msg);
|
|
||||||
static std::string fatalLogToString(const LogMessage& msg);
|
|
||||||
static std::string fatalCheckToString(const LogMessage& msg);
|
|
||||||
static std::string normalToString(const LogMessage& msg);
|
|
||||||
|
|
||||||
// the default formatting option
|
|
||||||
static std::string DefaultLogDetailsToString(const LogMessage& msg);
|
|
||||||
|
|
||||||
// this function can be used by the logging sink to add thread ID
|
|
||||||
// see this concept and it is easy to make your own custom formatting
|
|
||||||
static std::string FullLogDetailsToString(const LogMessage& msg);
|
|
||||||
|
|
||||||
using LogDetailsFunc = std::string (*)(const LogMessage&);
|
|
||||||
std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const;
|
|
||||||
|
|
||||||
void overrideLogDetailsFunc(LogDetailsFunc func) const;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Complete access to the raw data in case the helper functions above
|
|
||||||
// are not enough.
|
|
||||||
//
|
|
||||||
mutable LogDetailsFunc _logDetailsToStringFunc;
|
|
||||||
g3::high_resolution_time_point _timestamp;
|
|
||||||
std::thread::id _call_thread_id;
|
|
||||||
std::string _file;
|
|
||||||
std::string _file_path;
|
|
||||||
int _line;
|
|
||||||
std::string _function;
|
|
||||||
LEVELS _level;
|
|
||||||
std::string _expression; // only with content for CHECK(...) calls
|
|
||||||
mutable std::string _message;
|
|
||||||
|
|
||||||
friend void swap(LogMessage& first, LogMessage& second) {
|
|
||||||
using std::swap;
|
|
||||||
swap(first._timestamp, second._timestamp);
|
|
||||||
swap(first._call_thread_id, second._call_thread_id);
|
|
||||||
swap(first._file, second._file);
|
|
||||||
swap(first._line, second._line);
|
|
||||||
swap(first._function, second._function);
|
|
||||||
swap(first._level, second._level);
|
|
||||||
swap(first._expression, second._expression);
|
|
||||||
swap(first._message, second._message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Trigger for flushing the message queue and exiting the application
|
|
||||||
* A thread that causes a FatalMessage will sleep forever until the
|
|
||||||
* application has exited (after message flush) */
|
|
||||||
struct FatalMessage : public LogMessage {
|
|
||||||
FatalMessage(const LogMessage& details, g3::SignalType signal_id);
|
|
||||||
FatalMessage(const FatalMessage&);
|
|
||||||
FatalMessage& operator=(const FatalMessage&) = delete;
|
|
||||||
virtual ~FatalMessage() {}
|
|
||||||
|
|
||||||
LogMessage copyToLogMessage() const;
|
|
||||||
std::string reason() const;
|
|
||||||
|
|
||||||
const SignalType _signal_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef MoveOnCopy<std::unique_ptr<FatalMessage>> FatalMessagePtr;
|
|
||||||
typedef MoveOnCopy<std::unique_ptr<LogMessage>> LogMessagePtr;
|
|
||||||
typedef MoveOnCopy<LogMessage> LogMessageMover;
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
/** ==========================================================================
|
|
||||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
* Filename:g3logworker.h Framework for Logging and Design By Contract
|
|
||||||
* Created: 2011 by Kjell Hedström
|
|
||||||
*
|
|
||||||
* PUBLIC DOMAIN and Not copyrighted. First published at KjellKod.cc
|
|
||||||
* ********************************************* */
|
|
||||||
#include <memory>
|
|
||||||
#include "g3log/filesink.hpp"
|
|
||||||
#include "g3log/g3log.hpp"
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
#include "g3log/sinkhandle.hpp"
|
|
||||||
#include "g3log/sinkwrapper.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
class LogWorker;
|
|
||||||
struct LogWorkerImpl;
|
|
||||||
using FileSinkHandle = g3::SinkHandle<g3::FileSink>;
|
|
||||||
|
|
||||||
/// Background side of the LogWorker. Internal use only
|
|
||||||
struct LogWorkerImpl final {
|
|
||||||
typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
|
|
||||||
std::vector<SinkWrapperPtr> _sinks;
|
|
||||||
std::unique_ptr<kjellkod::Active> _bg; // do not change declaration order. _bg must be destroyed before sinks
|
|
||||||
|
|
||||||
LogWorkerImpl();
|
|
||||||
~LogWorkerImpl() = default;
|
|
||||||
|
|
||||||
void bgSave(g3::LogMessagePtr msgPtr);
|
|
||||||
void bgFatal(FatalMessagePtr msgPtr);
|
|
||||||
|
|
||||||
LogWorkerImpl(const LogWorkerImpl&) = delete;
|
|
||||||
LogWorkerImpl& operator=(const LogWorkerImpl&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Front end of the LogWorker. API that is useful is
|
|
||||||
/// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example
|
|
||||||
/// save( msg ) : internal use
|
|
||||||
/// fatal ( fatal_msg ) : internal use
|
|
||||||
class LogWorker final {
|
|
||||||
LogWorker() = default;
|
|
||||||
void addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> wrapper);
|
|
||||||
|
|
||||||
LogWorkerImpl _impl;
|
|
||||||
LogWorker(const LogWorker&) = delete;
|
|
||||||
LogWorker& operator=(const LogWorker&) = delete;
|
|
||||||
|
|
||||||
public:
|
|
||||||
~LogWorker();
|
|
||||||
|
|
||||||
/// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it
|
|
||||||
/// if you want to use the default file logger then see below for @ref addDefaultLogger
|
|
||||||
static std::unique_ptr<LogWorker> createLogWorker();
|
|
||||||
|
|
||||||
/**
|
|
||||||
A convenience function to add the default g3::FileSink to the log worker
|
|
||||||
@param log_prefix that you want
|
|
||||||
@param log_directory where the log is to be stored.
|
|
||||||
@return a handle for API access to the sink. See the README for example usage
|
|
||||||
|
|
||||||
@verbatim
|
|
||||||
Example:
|
|
||||||
using namespace g3;
|
|
||||||
std::unique_ptr<LogWorker> logworker {LogWorker::createLogWorker()};
|
|
||||||
auto handle = addDefaultLogger("my_test_log", "/tmp");
|
|
||||||
initializeLogging(logworker.get()); // ref. g3log.hpp
|
|
||||||
|
|
||||||
std::future<std::string> log_file_name = sinkHandle->call(&FileSink::fileName);
|
|
||||||
std::cout << "The filename is: " << log_file_name.get() << std::endl;
|
|
||||||
// something like: /tmp/my_test_log.g3log.20150819-100300.log
|
|
||||||
*/
|
|
||||||
std::unique_ptr<FileSinkHandle> addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log");
|
|
||||||
|
|
||||||
/// Adds a sink and returns the handle for access to the sink
|
|
||||||
/// @param real_sink unique_ptr ownership is passed to the log worker
|
|
||||||
/// @param call the default call that should receive either a std::string or a LogMessageMover message
|
|
||||||
/// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger
|
|
||||||
template <typename T, typename DefaultLogCall>
|
|
||||||
std::unique_ptr<g3::SinkHandle<T>> addSink(std::unique_ptr<T> real_sink, DefaultLogCall call) {
|
|
||||||
using namespace g3;
|
|
||||||
using namespace g3::internal;
|
|
||||||
auto sink = std::make_shared<Sink<T>>(std::move(real_sink), call);
|
|
||||||
addWrappedSink(sink);
|
|
||||||
return std::make_unique<SinkHandle<T>>(sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a sink. This is a synchronous call.
|
|
||||||
/// You are guaranteed that the sink is removed by the time the call returns
|
|
||||||
/// @param sink_handle the ownership of the sink handle is given
|
|
||||||
template <typename T>
|
|
||||||
void removeSink(std::unique_ptr<SinkHandle<T>> sink_handle) {
|
|
||||||
if (sink_handle) {
|
|
||||||
// sink_handle->sink().use_count() is 1 at this point
|
|
||||||
// i.e. this would be safe as long as no other weak_ptr to shared_ptr conversion
|
|
||||||
// was made by the client: assert(sink_handle->sink().use_count() == 0);
|
|
||||||
auto weak_ptr_sink = sink_handle->sink();
|
|
||||||
{
|
|
||||||
auto bg_removesink_call = [this, weak_ptr_sink] {
|
|
||||||
auto shared_sink = weak_ptr_sink.lock();
|
|
||||||
if (shared_sink) {
|
|
||||||
_impl._sinks.erase(std::remove(_impl._sinks.begin(), _impl._sinks.end(), shared_sink), _impl._sinks.end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
auto token_done = g3::spawn_task(bg_removesink_call, _impl._bg.get());
|
|
||||||
token_done.wait();
|
|
||||||
}
|
|
||||||
// sink_handle->sink().use_count() is 1 at this point.
|
|
||||||
// i.e. this would be safe: assert(sink_handle->sink().use_count() == 0);
|
|
||||||
// as long as the client has not converted more instances from the weak_ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink
|
|
||||||
/// handle then the sink will be removed internally but will live on in the client's instance
|
|
||||||
void removeAllSinks() {
|
|
||||||
auto bg_clear_sink_call = [this]() noexcept {
|
|
||||||
_impl._sinks.clear();
|
|
||||||
};
|
|
||||||
auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
|
|
||||||
token_cleared.wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// internal:
|
|
||||||
/// pushes in background thread (asynchronously) input messages to log file
|
|
||||||
void save(LogMessagePtr entry);
|
|
||||||
|
|
||||||
/// internal:
|
|
||||||
// pushes a fatal message on the queue, this is the last message to be processed
|
|
||||||
/// this way it's ensured that all existing entries were flushed before 'fatal'
|
|
||||||
/// Will abort the application!
|
|
||||||
void fatal(FatalMessagePtr fatal_message);
|
|
||||||
};
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
// A straightforward technique to move around packaged_tasks.
|
|
||||||
// Instances of std::packaged_task are MoveConstructible and MoveAssignable, but
|
|
||||||
// not CopyConstructible or CopyAssignable. To put them in a std container they need
|
|
||||||
// to be wrapped and their internals "moved" when tried to be copied.
|
|
||||||
|
|
||||||
template <typename Moveable>
|
|
||||||
struct MoveOnCopy {
|
|
||||||
mutable Moveable _move_only;
|
|
||||||
|
|
||||||
explicit MoveOnCopy(Moveable&& m) :
|
|
||||||
_move_only(std::move(m)) {}
|
|
||||||
MoveOnCopy(MoveOnCopy const& t) :
|
|
||||||
_move_only(std::move(t._move_only)) {}
|
|
||||||
MoveOnCopy(MoveOnCopy&& t) :
|
|
||||||
_move_only(std::move(t._move_only)) {}
|
|
||||||
|
|
||||||
MoveOnCopy& operator=(MoveOnCopy const& other) {
|
|
||||||
_move_only = std::move(other._move_only);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
MoveOnCopy& operator=(MoveOnCopy&& other) {
|
|
||||||
_move_only = std::move(other._move_only);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()() {
|
|
||||||
_move_only();
|
|
||||||
}
|
|
||||||
|
|
||||||
Moveable& get() {
|
|
||||||
return _move_only;
|
|
||||||
}
|
|
||||||
|
|
||||||
Moveable release() {
|
|
||||||
return std::move(_move_only);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
*
|
|
||||||
* Example of a normal std::queue protected by a mutex for operations,
|
|
||||||
* making it safe for thread communication, using std::mutex from C++0x with
|
|
||||||
* the help from the std::thread library from JustSoftwareSolutions
|
|
||||||
* ref: http://www.stdthread.co.uk/doc/headers/mutex.html
|
|
||||||
*
|
|
||||||
* This example was totally inspired by Anthony Williams lock-based data structures in
|
|
||||||
* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <exception>
|
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
/** Multiple producer, multiple consumer thread safe queue
|
|
||||||
* Since 'return by reference' is used this queue won't throw */
|
|
||||||
template <typename T>
|
|
||||||
class shared_queue {
|
|
||||||
std::queue<T> queue_;
|
|
||||||
mutable std::mutex m_;
|
|
||||||
std::condition_variable data_cond_;
|
|
||||||
|
|
||||||
shared_queue& operator=(const shared_queue&) = delete;
|
|
||||||
shared_queue(const shared_queue& other) = delete;
|
|
||||||
|
|
||||||
public:
|
|
||||||
shared_queue() = default;
|
|
||||||
|
|
||||||
void push(T item) {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_);
|
|
||||||
queue_.push(std::move(item));
|
|
||||||
}
|
|
||||||
data_cond_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \return immediately, with true if successful retrieval
|
|
||||||
bool try_and_pop(T& popped_item) {
|
|
||||||
std::lock_guard<std::mutex> lock(m_);
|
|
||||||
if (queue_.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
popped_item = std::move(queue_.front());
|
|
||||||
queue_.pop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to retrieve, if no items, wait till an item is available and try again
|
|
||||||
void wait_and_pop(T& popped_item) {
|
|
||||||
std::unique_lock<std::mutex> lock(m_);
|
|
||||||
while (queue_.empty()) {
|
|
||||||
data_cond_.wait(lock);
|
|
||||||
// This 'while' loop is equal to
|
|
||||||
// data_cond_.wait(lock, [](bool result){return !queue_.empty();});
|
|
||||||
}
|
|
||||||
popped_item = std::move(queue_.front());
|
|
||||||
queue_.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const {
|
|
||||||
std::lock_guard<std::mutex> lock(m_);
|
|
||||||
return queue_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned size() const {
|
|
||||||
std::lock_guard<std::mutex> lock(m_);
|
|
||||||
return queue_.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "g3log/active.hpp"
|
|
||||||
#include "g3log/future.hpp"
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
#include "g3log/sinkwrapper.hpp"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
typedef std::function<void(LogMessageMover)> AsyncMessageCall;
|
|
||||||
|
|
||||||
/// The asynchronous Sink has an active object, incoming requests for actions
|
|
||||||
// will be processed in the background by the specific object the Sink represents.
|
|
||||||
//
|
|
||||||
// The Sink will wrap either
|
|
||||||
// a Sink with Message object receiving call
|
|
||||||
// or a Sink with a LogEntry (string) receiving call
|
|
||||||
//
|
|
||||||
// The Sink can also be used through the SinkHandler to call Sink specific function calls
|
|
||||||
// Ref: send(Message) deals with incoming log entries (converted if necessary to string)
|
|
||||||
// Ref: send(Call call, Args... args) deals with calls
|
|
||||||
// to the real sink's API
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct Sink : public SinkWrapper {
|
|
||||||
std::unique_ptr<T> _real_sink;
|
|
||||||
std::unique_ptr<kjellkod::Active> _bg;
|
|
||||||
AsyncMessageCall _default_log_call;
|
|
||||||
|
|
||||||
template <typename DefaultLogCall>
|
|
||||||
Sink(std::unique_ptr<T> sink, DefaultLogCall call) :
|
|
||||||
SinkWrapper(),
|
|
||||||
_real_sink{std::move(sink)},
|
|
||||||
_bg(kjellkod::Active::createActive()),
|
|
||||||
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Sink(std::unique_ptr<T> sink, void (T::*Call)(std::string)) :
|
|
||||||
SinkWrapper(),
|
|
||||||
_real_sink{std::move(sink)},
|
|
||||||
_bg(kjellkod::Active::createActive()) {
|
|
||||||
std::function<void(std::string)> adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1);
|
|
||||||
_default_log_call = [=](LogMessageMover m) {
|
|
||||||
adapter(m.get().toString());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Sink() {
|
|
||||||
_bg.reset(); // TODO: to remove
|
|
||||||
}
|
|
||||||
|
|
||||||
void send(LogMessageMover msg) override {
|
|
||||||
_bg->send([this, msg] {
|
|
||||||
_default_log_call(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Call, typename... Args>
|
|
||||||
auto async(Call call, Args&&... args) -> std::future<std::invoke_result_t<decltype(call), T, Args...>> {
|
|
||||||
return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "g3log/sink.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
// The Sinkhandle is the client's access point to the specific sink instance.
|
|
||||||
// Only through the Sinkhandle can, and should, the real sink's specific API
|
|
||||||
// be called.
|
|
||||||
//
|
|
||||||
// The real sink will be owned by g3log. If the real sink is deleted
|
|
||||||
// calls to sink's API through the SinkHandle will return an exception embedded
|
|
||||||
// in the resulting future. Ref: SinkHandle::call
|
|
||||||
template <class T>
|
|
||||||
class SinkHandle {
|
|
||||||
std::weak_ptr<internal::Sink<T>> _sink;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SinkHandle(std::shared_ptr<internal::Sink<T>> sink) :
|
|
||||||
_sink(sink) {}
|
|
||||||
|
|
||||||
~SinkHandle() = default;
|
|
||||||
|
|
||||||
// Asynchronous call to the real sink. If the real sink is already deleted
|
|
||||||
// the returned future will contain a bad_weak_ptr exception instead of the
|
|
||||||
// call result.
|
|
||||||
template <typename AsyncCall, typename... Args>
|
|
||||||
auto call(AsyncCall func, Args&&... args) -> std::future<std::invoke_result_t<decltype(func), T, Args...>> {
|
|
||||||
try {
|
|
||||||
std::shared_ptr<internal::Sink<T>> sink(_sink);
|
|
||||||
return sink->async(func, std::forward<Args>(args)...);
|
|
||||||
} catch (const std::bad_weak_ptr& e) {
|
|
||||||
typedef std::invoke_result_t<decltype(func), T, Args...> PromiseType;
|
|
||||||
std::promise<PromiseType> promise;
|
|
||||||
promise.set_exception(std::make_exception_ptr(e));
|
|
||||||
return std::move(promise.get_future());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get weak_ptr access to the sink(). Make sure to check that the returned pointer is valid,
|
|
||||||
/// auto p = sink(); auto ptr = p.lock(); if (ptr) { .... }
|
|
||||||
/// ref: https://en.cppreference.com/w/cpp/memory/weak_ptr/lock
|
|
||||||
std::weak_ptr<internal::Sink<T>> sink() {
|
|
||||||
return _sink.lock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
struct SinkWrapper {
|
|
||||||
virtual ~SinkWrapper() {}
|
|
||||||
virtual void send(LogMessageMover msg) = 0;
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2014 by KjellKod.cc AND Robert Engeln.
|
|
||||||
* The stacktrace code was given as a public domain dedication by Robert Engeln
|
|
||||||
* It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
|
|
||||||
* It was (here) modified for g3log purposes.
|
|
||||||
*
|
|
||||||
* This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
#error "stacktrace_win.cpp used but not on a windows system"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace stacktrace {
|
|
||||||
/// return the text description of a Windows exception code
|
|
||||||
std::string exceptionIdToText(g3::SignalType id);
|
|
||||||
|
|
||||||
/// return whether or not the exception is a known exception, i.e.
|
|
||||||
/// an exception that we should treat as a fatal event
|
|
||||||
bool isKnownException(g3::SignalType id);
|
|
||||||
|
|
||||||
/// helper function: retrieve stackdump from no excisting exception pointer
|
|
||||||
std::string stackdump();
|
|
||||||
|
|
||||||
/// helper function: retrieve stackdump, starting from an exception pointer
|
|
||||||
std::string stackdump(EXCEPTION_POINTERS* info);
|
|
||||||
|
|
||||||
/// main stackdump function. retrieve stackdump, from the given context
|
|
||||||
std::string stackdump(CONTEXT* context);
|
|
||||||
|
|
||||||
} // namespace stacktrace
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well
|
|
||||||
* std::packaged_task<void()>. Thanks to Michael Rasmussen (lap777)
|
|
||||||
* Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800)
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <class... _ArgTypes>
|
|
||||||
class packaged_task<void(_ArgTypes...)> {
|
|
||||||
promise<void> _my_promise;
|
|
||||||
function<void(_ArgTypes...)> _my_func;
|
|
||||||
|
|
||||||
public:
|
|
||||||
packaged_task() {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class _Fty2>
|
|
||||||
explicit packaged_task(_Fty2&& _Fnarg) :
|
|
||||||
_my_func(_Fnarg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
packaged_task(packaged_task&& _Other) :
|
|
||||||
_my_promise(move(_Other._my_promise)),
|
|
||||||
_my_func(move(_Other._my_func)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
packaged_task& operator=(packaged_task&& _Other) {
|
|
||||||
_my_promise = move(_Other._my_promise);
|
|
||||||
_my_func = move(_Other._my_func);
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
packaged_task(const packaged_task&) = delete;
|
|
||||||
packaged_task& operator=(const packaged_task&) = delete;
|
|
||||||
|
|
||||||
~packaged_task() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(packaged_task& _Other) {
|
|
||||||
swap(_my_promise, _Other._my_promise);
|
|
||||||
swap(_my_func, _Other._my_func);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return _my_func != false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool valid() const {
|
|
||||||
return _my_func != false;
|
|
||||||
}
|
|
||||||
|
|
||||||
future<void> get_future() {
|
|
||||||
return _my_promise.get_future();
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(_ArgTypes... _Args) {
|
|
||||||
_my_func(forward<_ArgTypes>(_Args)...);
|
|
||||||
_my_promise.set_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
_my_promise.swap(promise<void>());
|
|
||||||
_my_func.swap(function<void(_ArgTypes...)>());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}; // namespace std
|
|
||||||
#endif // defined(WIN32) ...
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================
|
|
||||||
* Filename:g3time.h cross-platform, thread-safe replacement for C++11 non-thread-safe
|
|
||||||
* localtime (and similar)
|
|
||||||
* Created: 2012 by Kjell Hedström
|
|
||||||
*
|
|
||||||
* PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc
|
|
||||||
* ********************************************* */
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <ctime>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// FYI:
|
|
||||||
// namespace g3::internal ONLY in g3time.cpp
|
|
||||||
// std::string put_time(const struct tm* tmb, const char* c_time_format)
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
typedef std::chrono::time_point<std::chrono::system_clock> system_time_point;
|
|
||||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock> high_resolution_time_point;
|
|
||||||
typedef std::chrono::milliseconds milliseconds;
|
|
||||||
typedef std::chrono::microseconds microseconds;
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
enum class Fractional { Millisecond,
|
|
||||||
Microsecond,
|
|
||||||
Nanosecond,
|
|
||||||
NanosecondDefault };
|
|
||||||
Fractional getFractional(const std::string& format_buffer, size_t pos);
|
|
||||||
std::string to_string(const g3::system_time_point& ts, Fractional fractional);
|
|
||||||
std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer);
|
|
||||||
static const std::string date_formatted = "%Y/%m/%d";
|
|
||||||
// %f: fractions of seconds (%f is nanoseconds)
|
|
||||||
// %f3: milliseconds, 3 digits: 001
|
|
||||||
// %6: microseconds: 6 digits: 000001 --- default for the time_format
|
|
||||||
// %f9, %f: nanoseconds, 9 digits: 000000001
|
|
||||||
static const std::string time_formatted = "%H:%M:%S %f6";
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
|
|
||||||
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
|
|
||||||
// return value is SIMPLIFIED to only return a std::string
|
|
||||||
std::string put_time(const struct tm* tmb, const char* c_time_format);
|
|
||||||
|
|
||||||
/** return time representing POD struct (ref ctime + wchar) that is normally
|
|
||||||
* retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not.
|
|
||||||
* g3::localtime is probably used together with @ref g3::systemtime_now */
|
|
||||||
tm localtime(std::time_t time);
|
|
||||||
|
|
||||||
/** format string must conform to std::put_time's demands.
|
|
||||||
* WARNING: At time of writing there is only so-so compiler support for
|
|
||||||
* std::put_time. A possible fix if your c++11 library is not updated is to
|
|
||||||
* modify this to use std::strftime instead */
|
|
||||||
std::string localtime_formatted(const system_time_point& ts, const std::string& time_format);
|
|
||||||
|
|
||||||
inline system_time_point to_system_time(const high_resolution_time_point& ts) {
|
|
||||||
// On some (windows) systems, the system_clock does not provide the highest possible time
|
|
||||||
// resolution. Thus g3log uses high_resolution_clock for message time stamps. However,
|
|
||||||
// unlike system_clock, high_resolution_clock cannot be converted to a time and date as
|
|
||||||
// it usually measures reflects the time since power-up.
|
|
||||||
// Thus, hrs_now and sys_now are recorded once when the program starts to be able to convert
|
|
||||||
// timestamps to dime and date using to_system_time(). The precision of the absolute time is
|
|
||||||
// of course that of system_clock() with some error added due to the non-simultaneous initialization
|
|
||||||
// of the two static variables but relative times within one log will be as precise as
|
|
||||||
// high_resolution_clock.
|
|
||||||
using namespace std::chrono;
|
|
||||||
static const auto hrs_now = high_resolution_clock::now();
|
|
||||||
static const auto sys_now = system_clock::now();
|
|
||||||
|
|
||||||
return time_point_cast<system_clock::duration>(sys_now + (ts - hrs_now));
|
|
||||||
}
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/logcapture.hpp"
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/g3log.hpp"
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
|
||||||
#include <vector>
|
|
||||||
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
|
|
||||||
|
|
||||||
// For Windows we need force a thread_local install per thread of three
|
|
||||||
// signals that must have a signal handler installed per thread-basis
|
|
||||||
// It is really a royal pain. Seriously Microsoft? Seriously?
|
|
||||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread()
|
|
||||||
#else
|
|
||||||
// Does nothing --- enforces that semicolon must be written
|
|
||||||
#define SIGNAL_HANDLER_VERIFY() \
|
|
||||||
do { \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
|
||||||
// MaxMessageSize is message limit used with vsnprintf/vsnprintf_s
|
|
||||||
static int MaxMessageSize = 2048;
|
|
||||||
|
|
||||||
void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) {
|
|
||||||
MaxMessageSize = max_size;
|
|
||||||
}
|
|
||||||
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
|
|
||||||
|
|
||||||
/** logCapture is a simple struct for capturing log/fatal entries. At destruction the
|
|
||||||
* captured message is forwarded to background worker.
|
|
||||||
* As a safety precaution: No memory allocated here will be moved into the background
|
|
||||||
* worker in case of dynamic loaded library reasons instead the arguments are copied
|
|
||||||
* inside of g3log.cpp::saveMessage*/
|
|
||||||
LogCapture::~LogCapture() noexcept(false) {
|
|
||||||
using namespace g3::internal;
|
|
||||||
SIGNAL_HANDLER_VERIFY();
|
|
||||||
saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
|
|
||||||
LogCapture::LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump) :
|
|
||||||
LogCapture("", 0, "", level, "", fatal_signal, dump) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file, line, function are given in g3log.hpp from macros
|
|
||||||
* @level INFO/DEBUG/WARNING/FATAL
|
|
||||||
* @expression for CHECK calls
|
|
||||||
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
|
|
||||||
*/
|
|
||||||
LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS& level,
|
|
||||||
const char* expression, g3::SignalType fatal_signal, const char* dump) :
|
|
||||||
_file(file),
|
|
||||||
_line(line),
|
|
||||||
_function(function),
|
|
||||||
_level(level),
|
|
||||||
_expression(expression),
|
|
||||||
_fatal_signal(fatal_signal) {
|
|
||||||
|
|
||||||
if (g3::internal::wasFatal(level)) {
|
|
||||||
_stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"};
|
|
||||||
_stack_trace.append(g3::internal::stackdump(dump));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
|
|
||||||
* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18
|
|
||||||
*/
|
|
||||||
void LogCapture::capturef(const char* printf_like_message, ...) {
|
|
||||||
static const std::string kTruncatedWarningText = "[...truncated...]";
|
|
||||||
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
|
||||||
std::vector<char> finished_message_backing(MaxMessageSize);
|
|
||||||
char* finished_message = finished_message_backing.data();
|
|
||||||
auto finished_message_len = MaxMessageSize;
|
|
||||||
#else
|
|
||||||
static const int kMaxMessageSize = 2048;
|
|
||||||
char finished_message[kMaxMessageSize];
|
|
||||||
#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__))
|
|
||||||
auto finished_message_len = _countof(finished_message);
|
|
||||||
#else
|
|
||||||
int finished_message_len = sizeof(finished_message);
|
|
||||||
#endif
|
|
||||||
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE*/
|
|
||||||
|
|
||||||
va_list arglist;
|
|
||||||
va_start(arglist, printf_like_message);
|
|
||||||
|
|
||||||
#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__))
|
|
||||||
const int nbrcharacters = vsnprintf_s(finished_message, finished_message_len, _TRUNCATE, printf_like_message, arglist);
|
|
||||||
#else
|
|
||||||
const int nbrcharacters = vsnprintf(finished_message, finished_message_len, printf_like_message, arglist);
|
|
||||||
#endif
|
|
||||||
va_end(arglist);
|
|
||||||
|
|
||||||
if (nbrcharacters < 0) {
|
|
||||||
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to successfully parse the message";
|
|
||||||
stream() << '"' << printf_like_message << '"' << std::endl;
|
|
||||||
} else if (nbrcharacters > finished_message_len) {
|
|
||||||
stream() << finished_message << kTruncatedWarningText;
|
|
||||||
} else {
|
|
||||||
stream() << finished_message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/loglevels.hpp"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
bool wasFatal(const LEVELS& level) {
|
|
||||||
return level.value >= FATAL.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_LOGGING
|
|
||||||
const std::map<int, LoggingLevel> g_log_level_defaults = {
|
|
||||||
{G3LOG_DEBUG.value, {G3LOG_DEBUG}},
|
|
||||||
{INFO.value, {INFO}},
|
|
||||||
{WARNING.value, {WARNING}},
|
|
||||||
{FATAL.value, {FATAL}}};
|
|
||||||
|
|
||||||
std::map<int, g3::LoggingLevel> g_log_levels = g_log_level_defaults;
|
|
||||||
#endif
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
#ifdef G3_DYNAMIC_LOGGING
|
|
||||||
namespace only_change_at_initialization {
|
|
||||||
|
|
||||||
void addLogLevel(LEVELS lvl, bool enabled) {
|
|
||||||
int value = lvl.value;
|
|
||||||
internal::g_log_levels[value] = {lvl, enabled};
|
|
||||||
}
|
|
||||||
|
|
||||||
void addLogLevel(LEVELS level) {
|
|
||||||
addLogLevel(level, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
g3::internal::g_log_levels = g3::internal::g_log_level_defaults;
|
|
||||||
}
|
|
||||||
} // namespace only_change_at_initialization
|
|
||||||
|
|
||||||
namespace log_levels {
|
|
||||||
|
|
||||||
void setHighest(LEVELS enabledFrom) {
|
|
||||||
auto it = internal::g_log_levels.find(enabledFrom.value);
|
|
||||||
if (it != internal::g_log_levels.end()) {
|
|
||||||
for (auto& v : internal::g_log_levels) {
|
|
||||||
if (v.first < enabledFrom.value) {
|
|
||||||
disable(v.second.level);
|
|
||||||
} else {
|
|
||||||
enable(v.second.level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(LEVELS level, bool enabled) {
|
|
||||||
auto it = internal::g_log_levels.find(level.value);
|
|
||||||
if (it != internal::g_log_levels.end()) {
|
|
||||||
internal::g_log_levels[level.value] = {level, enabled};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable(LEVELS level) {
|
|
||||||
set(level, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable(LEVELS level) {
|
|
||||||
set(level, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void disableAll() {
|
|
||||||
for (auto& v : internal::g_log_levels) {
|
|
||||||
v.second.status = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void enableAll() {
|
|
||||||
for (auto& v : internal::g_log_levels) {
|
|
||||||
v.second.status = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint) {
|
|
||||||
std::string levels;
|
|
||||||
for (auto& v : levelsToPrint) {
|
|
||||||
levels += "name: " + v.second.level.text + " level: " + std::to_string(v.first) + " status: " + std::to_string(v.second.status.value()) + "\n";
|
|
||||||
}
|
|
||||||
return levels;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string to_string() {
|
|
||||||
return to_string(internal::g_log_levels);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, g3::LoggingLevel> getAll() {
|
|
||||||
return internal::g_log_levels;
|
|
||||||
}
|
|
||||||
|
|
||||||
// status : {Absent, Enabled, Disabled};
|
|
||||||
status getStatus(LEVELS level) {
|
|
||||||
const auto it = internal::g_log_levels.find(level.value);
|
|
||||||
if (internal::g_log_levels.end() == it) {
|
|
||||||
return status::Absent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (it->second.status.get().load() ? status::Enabled : status::Disabled);
|
|
||||||
}
|
|
||||||
} // namespace log_levels
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool logLevel(const LEVELS& log_level) {
|
|
||||||
#ifdef G3_DYNAMIC_LOGGING
|
|
||||||
int level = log_level.value;
|
|
||||||
bool status = internal::g_log_levels[level].status.value();
|
|
||||||
return status;
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
#include <mutex>
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/time.hpp"
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
std::string LogMessage::splitFileName(const std::string& str) {
|
|
||||||
size_t found;
|
|
||||||
found = str.find_last_of("(/\\");
|
|
||||||
return str.substr(found + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for fatal signal
|
|
||||||
std::string LogMessage::fatalSignalToString(const LogMessage& msg) {
|
|
||||||
std::string out; // clear any previous text and formatting
|
|
||||||
out.append(msg.timestamp() + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + msg.message() + '\n');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for fatal exception (windows only)
|
|
||||||
std::string LogMessage::fatalExceptionToString(const LogMessage& msg) {
|
|
||||||
std::string out; // clear any previous text and formatting
|
|
||||||
out.append(msg.timestamp() + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + msg.message() + '\n');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for fatal LOG
|
|
||||||
std::string LogMessage::fatalLogToString(const LogMessage& msg) {
|
|
||||||
auto out = msg._logDetailsToStringFunc(msg);
|
|
||||||
static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
|
|
||||||
out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for fatal CHECK
|
|
||||||
std::string LogMessage::fatalCheckToString(const LogMessage& msg) {
|
|
||||||
auto out = msg._logDetailsToStringFunc(msg);
|
|
||||||
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
|
|
||||||
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" + '"' + msg.message() + '"');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for setting the normal log details in an entry
|
|
||||||
std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) {
|
|
||||||
std::string out;
|
|
||||||
out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) {
|
|
||||||
std::string out;
|
|
||||||
out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.threadID() + " " + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for normal
|
|
||||||
std::string LogMessage::normalToString(const LogMessage& msg) {
|
|
||||||
auto out = msg._logDetailsToStringFunc(msg);
|
|
||||||
out.append(msg.message() + '\n');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// end static functions section
|
|
||||||
|
|
||||||
void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const {
|
|
||||||
_logDetailsToStringFunc = func;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the log message according to it's type
|
|
||||||
std::string LogMessage::toString(LogDetailsFunc formattingFunc) const {
|
|
||||||
overrideLogDetailsFunc(formattingFunc);
|
|
||||||
|
|
||||||
if (false == wasFatal()) {
|
|
||||||
return LogMessage::normalToString(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto level_value = _level.value;
|
|
||||||
if (internal::FATAL_SIGNAL.value == _level.value) {
|
|
||||||
return LogMessage::fatalSignalToString(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (internal::FATAL_EXCEPTION.value == _level.value) {
|
|
||||||
return LogMessage::fatalExceptionToString(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FATAL.value == _level.value) {
|
|
||||||
return LogMessage::fatalLogToString(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (internal::CONTRACT.value == level_value) {
|
|
||||||
return LogMessage::fatalCheckToString(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// What? Did we hit a custom made level?
|
|
||||||
auto out = _logDetailsToStringFunc(*this);
|
|
||||||
static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"};
|
|
||||||
out.append("\t*******" + errorUnknown + "\n\t" + message() + '\n');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string LogMessage::timestamp(const std::string& time_look) const {
|
|
||||||
return g3::localtime_formatted(to_system_time(_timestamp), time_look);
|
|
||||||
}
|
|
||||||
|
|
||||||
// By copy, not by reference. See this explanation for details:
|
|
||||||
// http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
|
|
||||||
LogMessage& LogMessage::operator=(LogMessage other) {
|
|
||||||
swap(*this, other);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage::LogMessage(std::string file, const int line,
|
|
||||||
std::string function, const LEVELS level) :
|
|
||||||
_logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString),
|
|
||||||
_timestamp(std::chrono::high_resolution_clock::now()),
|
|
||||||
_call_thread_id(std::this_thread::get_id())
|
|
||||||
#if defined(G3_LOG_FULL_FILENAME)
|
|
||||||
,
|
|
||||||
_file(file)
|
|
||||||
#else
|
|
||||||
,
|
|
||||||
_file(LogMessage::splitFileName(file))
|
|
||||||
#endif
|
|
||||||
,
|
|
||||||
_file_path(file),
|
|
||||||
_line(line),
|
|
||||||
_function(std::move(function)),
|
|
||||||
_level(level) {
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) :
|
|
||||||
LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) {
|
|
||||||
_message.append(fatalOsSignalCrashMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage::LogMessage(const LogMessage& other) :
|
|
||||||
_logDetailsToStringFunc(other._logDetailsToStringFunc),
|
|
||||||
_timestamp(other._timestamp),
|
|
||||||
_call_thread_id(other._call_thread_id),
|
|
||||||
_file(other._file),
|
|
||||||
_file_path(other._file_path),
|
|
||||||
_line(other._line),
|
|
||||||
_function(other._function),
|
|
||||||
_level(other._level),
|
|
||||||
_expression(other._expression),
|
|
||||||
_message(other._message) {
|
|
||||||
}
|
|
||||||
|
|
||||||
LogMessage::LogMessage(LogMessage&& other) :
|
|
||||||
_logDetailsToStringFunc(other._logDetailsToStringFunc),
|
|
||||||
_timestamp(other._timestamp),
|
|
||||||
_call_thread_id(other._call_thread_id),
|
|
||||||
_file(std::move(other._file)),
|
|
||||||
_file_path(std::move(other._file_path)),
|
|
||||||
_line(other._line),
|
|
||||||
_function(std::move(other._function)),
|
|
||||||
_level(other._level),
|
|
||||||
_expression(std::move(other._expression)),
|
|
||||||
_message(std::move(other._message)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string LogMessage::threadID() const {
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << _call_thread_id;
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id) :
|
|
||||||
LogMessage(details),
|
|
||||||
_signal_id(signal_id) {}
|
|
||||||
|
|
||||||
FatalMessage::FatalMessage(const FatalMessage& other) :
|
|
||||||
LogMessage(other),
|
|
||||||
_signal_id(other._signal_id) {}
|
|
||||||
|
|
||||||
LogMessage FatalMessage::copyToLogMessage() const {
|
|
||||||
return LogMessage(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FatalMessage::reason() const {
|
|
||||||
return internal::exitReasonName(_level, _signal_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/logworker.hpp"
|
|
||||||
#include "g3log/active.hpp"
|
|
||||||
#include "g3log/crashhandler.hpp"
|
|
||||||
#include "g3log/future.hpp"
|
|
||||||
#include "g3log/g3log.hpp"
|
|
||||||
#include "g3log/logmessage.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
|
|
||||||
LogWorkerImpl::LogWorkerImpl() :
|
|
||||||
_bg(kjellkod::Active::createActive()) {}
|
|
||||||
|
|
||||||
void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
|
|
||||||
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
|
|
||||||
|
|
||||||
for (auto& sink : _sinks) {
|
|
||||||
LogMessage msg(*(uniqueMsg));
|
|
||||||
sink->send(LogMessageMover(std::move(msg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sinks.empty()) {
|
|
||||||
std::string err_msg{"g3logworker has no sinks. Message: ["};
|
|
||||||
err_msg.append(uniqueMsg.get()->toString()).append("]\n");
|
|
||||||
std::cerr << err_msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) {
|
|
||||||
// this will be the last message. Only the active logworker can receive a FATAL call so it's
|
|
||||||
// safe to shutdown logging now
|
|
||||||
g3::internal::shutDownLogging();
|
|
||||||
|
|
||||||
std::string reason = msgPtr.get()->reason();
|
|
||||||
const auto level = msgPtr.get()->_level;
|
|
||||||
const auto fatal_id = msgPtr.get()->_signal_id;
|
|
||||||
|
|
||||||
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
|
|
||||||
uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level());
|
|
||||||
|
|
||||||
// Change output in case of a fatal signal (or windows exception)
|
|
||||||
std::string exiting = {"Fatal type: "};
|
|
||||||
|
|
||||||
uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason).append("\nLog content flushed successfully to sink\n\n");
|
|
||||||
|
|
||||||
std::cerr << uniqueMsg->toString() << std::flush;
|
|
||||||
for (auto& sink : _sinks) {
|
|
||||||
LogMessage msg(*(uniqueMsg));
|
|
||||||
sink->send(LogMessageMover(std::move(msg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This clear is absolutely necessary
|
|
||||||
// All sinks are forced to receive the fatal message above before we continue
|
|
||||||
_sinks.clear(); // flush all queues
|
|
||||||
internal::exitWithDefaultSignalHandler(level, fatal_id);
|
|
||||||
|
|
||||||
// should never reach this point
|
|
||||||
perror("g3log exited after receiving FATAL trigger. Flush message status: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
LogWorker::~LogWorker() {
|
|
||||||
g3::internal::shutDownLoggingForActiveOnly(this);
|
|
||||||
|
|
||||||
// The sinks WILL automatically be cleared at exit of this destructor
|
|
||||||
// The waiting inside removeAllSinks ensures that all messages until this point are
|
|
||||||
// taken care of before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
|
|
||||||
// i.e. this avoids a race with another thread slipping through the "shutdownLogging" and
|
|
||||||
// calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly
|
|
||||||
// deconstructed LogWorkerImpl"
|
|
||||||
//
|
|
||||||
// Any messages put into the queue will be OK due to:
|
|
||||||
// *) If it is before the wait below then they will be executed
|
|
||||||
// *) If it is AFTER the wait below then they will be ignored and NEVER executed
|
|
||||||
removeAllSinks();
|
|
||||||
|
|
||||||
// The background worker WILL be automatically cleared at the exit of the destructor
|
|
||||||
// However, the explicitly clearing of the background worker (below) makes sure that there can
|
|
||||||
// be no thread that manages to add another sink after the call to clear the sinks above.
|
|
||||||
// i.e. this manages the extremely unlikely case of another thread calling
|
|
||||||
// addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp
|
|
||||||
// and be closely coupled with the existence of the LogWorker. Sharing this adding of sinks to
|
|
||||||
// other threads that do not know the state of LogWorker is considered a bug but it is dealt with
|
|
||||||
// nonetheless below.
|
|
||||||
//
|
|
||||||
// If sinks would already have been added after the sink clear above then this reset will deal with it
|
|
||||||
// without risking lambda execution with a partially deconstructed LogWorkerImpl
|
|
||||||
// Calling g3::spawn_task on a nullptr Active object will not crash but return
|
|
||||||
// a future containing an appropriate exception.
|
|
||||||
_impl._bg.reset(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogWorker::save(LogMessagePtr msg) {
|
|
||||||
_impl._bg->send([this, msg] { _impl.bgSave(msg); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogWorker::fatal(FatalMessagePtr fatal_message) {
|
|
||||||
_impl._bg->send([this, fatal_message] { _impl.bgFatal(fatal_message); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) {
|
|
||||||
auto bg_addsink_call = [this, sink] {
|
|
||||||
_impl._sinks.push_back(sink);
|
|
||||||
};
|
|
||||||
auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
|
|
||||||
token_done.wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<LogWorker> LogWorker::createLogWorker() {
|
|
||||||
return std::unique_ptr<LogWorker>(new LogWorker);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<FileSinkHandle> LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
|
|
||||||
return addSink(std::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for
|
|
||||||
* the benefit of g3log. It was originally published at:
|
|
||||||
* http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
|
|
||||||
|
|
||||||
* 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod).
|
|
||||||
*
|
|
||||||
* This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/stacktrace_windows.hpp"
|
|
||||||
|
|
||||||
#include <dbghelp.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <cassert>
|
|
||||||
#include <g3log/g3log.hpp>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#pragma comment(lib, "dbghelp.lib")
|
|
||||||
|
|
||||||
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
#error "stacktrace_win.cpp used but not on a windows system"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define g3_MAP_PAIR_STRINGIFY(x) \
|
|
||||||
{ x, #x }
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
thread_local size_t g_thread_local_recursive_crash_check = 0;
|
|
||||||
|
|
||||||
const std::map<g3::SignalType, std::string> kExceptionsAsText = {
|
|
||||||
g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Using the given context, fill in all the stack frames.
|
|
||||||
// Which then later can be interpreted to human readable text
|
|
||||||
void captureStackTrace(CONTEXT* context, std::vector<uint64_t>& frame_pointers) {
|
|
||||||
DWORD machine_type = 0;
|
|
||||||
STACKFRAME64 frame = {}; // force zeroing
|
|
||||||
frame.AddrPC.Mode = AddrModeFlat;
|
|
||||||
frame.AddrFrame.Mode = AddrModeFlat;
|
|
||||||
frame.AddrStack.Mode = AddrModeFlat;
|
|
||||||
#if defined(_M_ARM64)
|
|
||||||
frame.AddrPC.Offset = context->Pc;
|
|
||||||
frame.AddrFrame.Offset = context->Fp;
|
|
||||||
frame.AddrStack.Offset = context->Sp;
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_ARM64;
|
|
||||||
#elif defined(_M_ARM)
|
|
||||||
frame.AddrPC.Offset = context->Pc;
|
|
||||||
frame.AddrFrame.Offset = context->R11;
|
|
||||||
frame.AddrStack.Offset = context->Sp;
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_ARM;
|
|
||||||
#elif defined(_M_X64)
|
|
||||||
frame.AddrPC.Offset = context->Rip;
|
|
||||||
frame.AddrFrame.Offset = context->Rbp;
|
|
||||||
frame.AddrStack.Offset = context->Rsp;
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_AMD64;
|
|
||||||
#else
|
|
||||||
frame.AddrPC.Offset = context->Eip;
|
|
||||||
frame.AddrPC.Offset = context->Ebp;
|
|
||||||
frame.AddrPC.Offset = context->Esp;
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_I386;
|
|
||||||
#endif
|
|
||||||
for (size_t index = 0; index < frame_pointers.size(); ++index) {
|
|
||||||
if (StackWalk64(machine_type,
|
|
||||||
GetCurrentProcess(),
|
|
||||||
GetCurrentThread(),
|
|
||||||
&frame,
|
|
||||||
context,
|
|
||||||
NULL,
|
|
||||||
SymFunctionTableAccess64,
|
|
||||||
SymGetModuleBase64,
|
|
||||||
NULL)) {
|
|
||||||
frame_pointers[index] = frame.AddrPC.Offset;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract readable text from a given stack frame. All thanks to
|
|
||||||
// using SymFromAddr and SymGetLineFromAddr64 with the stack pointer
|
|
||||||
std::string getSymbolInformation(const size_t index, const std::vector<uint64_t>& frame_pointers) {
|
|
||||||
auto addr = frame_pointers[index];
|
|
||||||
std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t";
|
|
||||||
|
|
||||||
DWORD64 displacement64;
|
|
||||||
DWORD displacement;
|
|
||||||
alignas(SYMBOL_INFO) char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
|
||||||
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer);
|
|
||||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
||||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
|
||||||
|
|
||||||
IMAGEHLP_LINE64 line;
|
|
||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
||||||
std::string lineInformation;
|
|
||||||
std::string callInformation;
|
|
||||||
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) {
|
|
||||||
callInformation.append(" ").append(std::string(symbol->Name, symbol->NameLen));
|
|
||||||
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) {
|
|
||||||
lineInformation.append("\t").append(line.FileName).append(" L: ");
|
|
||||||
lineInformation.append(std::to_string(line.LineNumber));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
frame_dump.append(lineInformation).append(callInformation);
|
|
||||||
return frame_dump;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves all the symbols for the stack frames, fills them within a text representation and returns it
|
|
||||||
std::string convertFramesToText(std::vector<uint64_t>& frame_pointers) {
|
|
||||||
std::string dump; // slightly more efficient than ostringstream
|
|
||||||
const size_t kSize = frame_pointers.size();
|
|
||||||
for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) {
|
|
||||||
dump += getSymbolInformation(index, frame_pointers);
|
|
||||||
dump += "\n";
|
|
||||||
}
|
|
||||||
return dump;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace stacktrace {
|
|
||||||
const std::string kUnknown = {"UNKNOWN EXCEPTION"};
|
|
||||||
/// return the text description of a Windows exception code
|
|
||||||
/// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
|
|
||||||
std::string exceptionIdToText(g3::SignalType id) {
|
|
||||||
const auto iter = kExceptionsAsText.find(id);
|
|
||||||
if (iter == kExceptionsAsText.end()) {
|
|
||||||
std::string unknown = {kUnknown + ":" + std::to_string(id)};
|
|
||||||
return unknown;
|
|
||||||
}
|
|
||||||
return iter->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Yes a double lookup: first for isKnownException and then exceptionIdToText
|
|
||||||
/// for vectored exceptions we only deal with known exceptions so this tiny
|
|
||||||
/// overhead we can live with
|
|
||||||
bool isKnownException(g3::SignalType id) {
|
|
||||||
return (kExceptionsAsText.end() != kExceptionsAsText.find(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// helper function: retrieve stackdump from no excisting exception pointer
|
|
||||||
std::string stackdump() {
|
|
||||||
CONTEXT current_context;
|
|
||||||
memset(¤t_context, 0, sizeof(CONTEXT));
|
|
||||||
RtlCaptureContext(¤t_context);
|
|
||||||
return stackdump(¤t_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// helper function: retrieve stackdump, starting from an exception pointer
|
|
||||||
std::string stackdump(EXCEPTION_POINTERS* info) {
|
|
||||||
auto context = info->ContextRecord;
|
|
||||||
return stackdump(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// main stackdump function. retrieve stackdump, from the given context
|
|
||||||
std::string stackdump(CONTEXT* context) {
|
|
||||||
|
|
||||||
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarios we allow one extra pass
|
|
||||||
std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
|
|
||||||
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
|
|
||||||
return recursive_crash;
|
|
||||||
}
|
|
||||||
++g_thread_local_recursive_crash_check;
|
|
||||||
|
|
||||||
static std::mutex m;
|
|
||||||
std::lock_guard<std::mutex> lock(m);
|
|
||||||
{
|
|
||||||
const BOOL kLoadSymModules = TRUE;
|
|
||||||
const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules);
|
|
||||||
if (TRUE != initialized) {
|
|
||||||
return {"Error: Cannot call SymInitialize(...) for retrieving symbols in stack"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<void> RaiiSymCleaner(nullptr, [&](void*) {
|
|
||||||
SymCleanup(GetCurrentProcess());
|
|
||||||
}); // Raii sym cleanup
|
|
||||||
|
|
||||||
constexpr size_t kmax_frame_dump_size = 64;
|
|
||||||
std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
|
|
||||||
// C++11: size set and values are zeroed
|
|
||||||
|
|
||||||
assert(frame_pointers.size() == kmax_frame_dump_size);
|
|
||||||
captureStackTrace(context, frame_pointers);
|
|
||||||
return convertFramesToText(frame_pointers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace stacktrace
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
/** ==========================================================================
|
|
||||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
|
||||||
* with no warranties. This code is yours to share, use and modify with no
|
|
||||||
* strings attached and no restrictions or obligations.
|
|
||||||
*
|
|
||||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
|
||||||
* ============================================================================*/
|
|
||||||
|
|
||||||
#include "g3log/time.hpp"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#ifdef __MACH__
|
|
||||||
#include <sys/time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
namespace internal {
|
|
||||||
const std::string kFractionalIdentier = "%f";
|
|
||||||
const size_t kFractionalIdentierSize = 2;
|
|
||||||
|
|
||||||
Fractional getFractional(const std::string& format_buffer, size_t pos) {
|
|
||||||
char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0');
|
|
||||||
Fractional type = Fractional::NanosecondDefault;
|
|
||||||
switch (ch) {
|
|
||||||
case '3':
|
|
||||||
type = Fractional::Millisecond;
|
|
||||||
break;
|
|
||||||
case '6':
|
|
||||||
type = Fractional::Microsecond;
|
|
||||||
break;
|
|
||||||
case '9':
|
|
||||||
type = Fractional::Nanosecond;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
type = Fractional::NanosecondDefault;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the fractional as a string with padded zeroes
|
|
||||||
// 1 ms --> 001
|
|
||||||
// 1 us --> 000001
|
|
||||||
// 1 ns --> 000000001
|
|
||||||
std::string to_string(const g3::system_time_point& ts, Fractional fractional) {
|
|
||||||
auto duration = ts.time_since_epoch();
|
|
||||||
auto sec_duration = std::chrono::duration_cast<std::chrono::seconds>(duration);
|
|
||||||
duration -= sec_duration;
|
|
||||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
|
|
||||||
|
|
||||||
auto zeroes = 9; // default ns
|
|
||||||
auto digitsToCut = 1; // default ns, divide by 1 makes no change
|
|
||||||
switch (fractional) {
|
|
||||||
case Fractional::Millisecond: {
|
|
||||||
zeroes = 3;
|
|
||||||
digitsToCut = 1000000;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Fractional::Microsecond: {
|
|
||||||
zeroes = 6;
|
|
||||||
digitsToCut = 1000;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Fractional::Nanosecond:
|
|
||||||
case Fractional::NanosecondDefault:
|
|
||||||
default:
|
|
||||||
zeroes = 9;
|
|
||||||
digitsToCut = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ns /= digitsToCut;
|
|
||||||
auto value = std::string(std::to_string(ns));
|
|
||||||
return std::string(zeroes - value.size(), '0') + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer) {
|
|
||||||
// iterating through every "%f" instance in the format string
|
|
||||||
auto identifierExtraSize = 0;
|
|
||||||
for (size_t pos = 0;
|
|
||||||
(pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos;
|
|
||||||
pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) {
|
|
||||||
// figuring out whether this is nano, micro or milli identifier
|
|
||||||
auto type = g3::internal::getFractional(format_buffer, pos);
|
|
||||||
auto value = g3::internal::to_string(ts, type);
|
|
||||||
auto padding = 0;
|
|
||||||
if (type != g3::internal::Fractional::NanosecondDefault) {
|
|
||||||
padding = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// replacing "%f[3|6|9]" with sec fractional part value
|
|
||||||
format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value);
|
|
||||||
}
|
|
||||||
return format_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace g3
|
|
||||||
|
|
||||||
namespace g3 {
|
|
||||||
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
|
|
||||||
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
|
|
||||||
// return value is SIMPLIFIED to only return a std::string
|
|
||||||
std::string put_time(const struct tm* tmb, const char* c_time_format) {
|
|
||||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss.fill('0');
|
|
||||||
// BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
|
|
||||||
oss << std::put_time(const_cast<struct tm*>(tmb), c_time_format);
|
|
||||||
return oss.str();
|
|
||||||
#else // LINUX
|
|
||||||
const size_t size = 1024;
|
|
||||||
char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time.
|
|
||||||
// ... also ... This is way more buffer space then we need
|
|
||||||
|
|
||||||
auto success = std::strftime(buffer, size, c_time_format, tmb);
|
|
||||||
// In DEBUG the assert will trigger a process exit. Once inside the if-statement
|
|
||||||
// the 'always true' expression will be displayed as reason for the exit
|
|
||||||
//
|
|
||||||
// In Production mode
|
|
||||||
// the assert will do nothing but the format string will instead be returned
|
|
||||||
if (0 == success) {
|
|
||||||
assert((0 != success) && "strftime fails with illegal formatting");
|
|
||||||
return c_time_format;
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
tm localtime(std::time_t ts) {
|
|
||||||
struct tm tm_snapshot;
|
|
||||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
|
||||||
localtime_s(&tm_snapshot, &ts); // windsows
|
|
||||||
#else
|
|
||||||
localtime_r(&ts, &tm_snapshot); // POSIX
|
|
||||||
#endif
|
|
||||||
return tm_snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) {
|
|
||||||
auto format_buffer = internal::localtime_formatted_fractions(ts, time_format);
|
|
||||||
auto time_point = std::chrono::system_clock::to_time_t(ts);
|
|
||||||
std::tm t = localtime(time_point);
|
|
||||||
return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
|
|
||||||
}
|
|
||||||
} // namespace g3
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common.hpp"
|
|
||||||
#include "g3log/g3log.hpp"
|
|
||||||
#include "g3log/logworker.hpp"
|
|
||||||
|
|
||||||
namespace peak
|
|
||||||
{
|
|
||||||
const LEVELS SUCCESS{200, {"SUCCESS"}};
|
|
||||||
const LEVELS ERR{800, "ERROR"};
|
|
||||||
|
|
||||||
class Logger;
|
|
||||||
inline Logger* logger{};
|
|
||||||
|
|
||||||
class Logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Logger() :
|
|
||||||
worker(g3::LogWorker::createLogWorker())
|
|
||||||
{
|
|
||||||
AllocConsole();
|
|
||||||
consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
SetConsoleTitleA("PeakMenu");
|
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
|
||||||
RemoveMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_DISABLED);
|
|
||||||
SetConsoleCtrlHandler([](DWORD) -> BOOL
|
|
||||||
{
|
|
||||||
isRunning = false;
|
|
||||||
return TRUE;
|
|
||||||
}, TRUE);
|
|
||||||
consoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app);
|
|
||||||
size_t size = MAX_PATH;
|
|
||||||
char* buffer;
|
|
||||||
_dupenv_s(&buffer, &size, "APPDATA");
|
|
||||||
filePath = (std::filesystem::path)buffer / "PeakMenu";
|
|
||||||
std::filesystem::path logPath = filePath / "logs";
|
|
||||||
if (!std::filesystem::exists(filePath))
|
|
||||||
{
|
|
||||||
std::filesystem::create_directory(filePath);
|
|
||||||
}
|
|
||||||
else if (!std::filesystem::is_directory(filePath))
|
|
||||||
{
|
|
||||||
std::filesystem::remove(filePath);
|
|
||||||
std::filesystem::create_directory(filePath);
|
|
||||||
}
|
|
||||||
if (!std::filesystem::exists(logPath))
|
|
||||||
{
|
|
||||||
std::filesystem::create_directory(logPath);
|
|
||||||
}
|
|
||||||
else if (!std::filesystem::is_directory(logPath))
|
|
||||||
{
|
|
||||||
std::filesystem::remove(logPath);
|
|
||||||
std::filesystem::create_directory(logPath);
|
|
||||||
}
|
|
||||||
filePath /= "PeakMenu.log";
|
|
||||||
if (std::filesystem::exists(filePath))
|
|
||||||
{
|
|
||||||
tm localTime;
|
|
||||||
time_t creationDate = std::chrono::system_clock::to_time_t(std::chrono::time_point_cast<std::chrono::system_clock::duration>(std::filesystem::last_write_time(filePath) - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()));
|
|
||||||
localtime_s(&localTime, &creationDate);
|
|
||||||
std::ostringstream day, month, year, hour, minute, second;
|
|
||||||
day << std::setfill('0') << std::setw(2) << localTime.tm_mday;
|
|
||||||
month << "-" << std::setfill('0') << std::setw(2) << localTime.tm_mon + 1;
|
|
||||||
year << "-" << std::setfill('0') << std::setw(4) << localTime.tm_year + 1900;
|
|
||||||
hour << " " << std::setfill('0') << std::setw(2) << localTime.tm_hour;
|
|
||||||
minute << "-" << std::setfill('0') << std::setw(2) << localTime.tm_min;
|
|
||||||
second << "-" << std::setfill('0') << std::setw(2) << localTime.tm_sec;
|
|
||||||
std::filesystem::copy_file(filePath, logPath / (day.str() + month.str() + year.str() + hour.str() + minute.str() + second.str()));
|
|
||||||
}
|
|
||||||
fileOut.open(filePath, std::ios_base::out | std::ios_base::trunc);
|
|
||||||
worker->addSink(std::make_unique<Sink>(), &Sink::callback);
|
|
||||||
g3::initializeLogging(worker.get());
|
|
||||||
g3::setFatalExitHandler(&Sink::fatalCallback);
|
|
||||||
logger = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Logger()
|
|
||||||
{
|
|
||||||
worker.reset();
|
|
||||||
FreeConsole();
|
|
||||||
logger = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Sink
|
|
||||||
{
|
|
||||||
std::map<std::string, WORD> colors = {{SUCCESS.text, FOREGROUND_GREEN | FOREGROUND_INTENSITY}, {INFO.text, FOREGROUND_BLUE | FOREGROUND_INTENSITY}, {WARNING.text, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY}, {ERR.text, FOREGROUND_RED | FOREGROUND_INTENSITY}, {FATAL.text, FOREGROUND_RED | FOREGROUND_INTENSITY}};
|
|
||||||
|
|
||||||
void callback(g3::LogMessageMover log)
|
|
||||||
{
|
|
||||||
g3::LogMessage msg = log.get();
|
|
||||||
SetConsoleTextAttribute(logger->consoleHandle, colors.find(msg.level())->second);
|
|
||||||
std::stringstream message(msg.toString([](const g3::LogMessage&)->std::string {return ""; }));
|
|
||||||
std::string line;
|
|
||||||
std::ostringstream fmt, fileFmt;
|
|
||||||
fmt << "[" << msg.timestamp("%d/%m/%Y %H:%M:%S") << "] ";
|
|
||||||
fileFmt << "[" << msg.timestamp("%d/%m/%Y %H:%M:%S") << "] [" << msg.level() << "] ";
|
|
||||||
while (std::getline(message, line, '\n'))
|
|
||||||
{
|
|
||||||
logger->consoleOut << fmt.str() << line << std::endl << std::flush;
|
|
||||||
if (std::filesystem::exists(logger->filePath) && !std::filesystem::is_directory(logger->filePath))
|
|
||||||
{
|
|
||||||
logger->fileOut << fileFmt.str() << line << std::endl << std::flush;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fatalCallback(g3::FatalMessagePtr log)
|
|
||||||
{
|
|
||||||
SetConsoleTextAttribute(logger->consoleHandle, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
||||||
std::stringstream message(log.get()->toString([](const g3::LogMessage&)->std::string {return ""; }));
|
|
||||||
std::string line;
|
|
||||||
std::ostringstream fmt, fileFmt;
|
|
||||||
fmt << "[" << log.get()->timestamp("%d/%m/%Y %H:%M:%S") << "] ";
|
|
||||||
fileFmt << "[" << log.get()->timestamp("%d/%m/%Y %H:%M:%S") << "] [FATAL] ";
|
|
||||||
while (std::getline(message, line, '\n'))
|
|
||||||
{
|
|
||||||
logger->consoleOut << fmt.str() << line << std::endl << std::flush;
|
|
||||||
if (std::filesystem::exists(logger->filePath) && !std::filesystem::is_directory(logger->filePath))
|
|
||||||
{
|
|
||||||
logger->fileOut << fileFmt.str() << line << std::endl << std::flush;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
HANDLE consoleHandle{};
|
|
||||||
std::ofstream consoleOut;
|
|
||||||
std::filesystem::path filePath;
|
|
||||||
std::ofstream fileOut;
|
|
||||||
std::unique_ptr<g3::LogWorker> worker;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#include "common.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
#include "pointers.hpp"
|
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HMODULE hmod, DWORD reason, PVOID)
|
|
||||||
{
|
|
||||||
using namespace peak;
|
|
||||||
if (reason == DLL_PROCESS_ATTACH)
|
|
||||||
{
|
|
||||||
hModule = hmod;
|
|
||||||
mainThread = CreateThread(nullptr, 0, [](PVOID) -> DWORD {
|
|
||||||
auto loggerInstance = std::make_unique<Logger>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto pointerInstance = std::make_unique<Pointers>();
|
|
||||||
LOG(SUCCESS) << "Patterns found.";
|
|
||||||
while (isRunning)
|
|
||||||
{
|
|
||||||
pointers->character->infiniteStamina = true;
|
|
||||||
std::this_thread::sleep_for(100ms);
|
|
||||||
}
|
|
||||||
pointers->character->infiniteStamina = false;
|
|
||||||
pointerInstance.reset();
|
|
||||||
LOG(SUCCESS) << "Removed patterns";
|
|
||||||
}
|
|
||||||
catch (std::exception& exc)
|
|
||||||
{
|
|
||||||
LOG(FATAL) << exc.what();
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(1s);
|
|
||||||
loggerInstance.reset();
|
|
||||||
CloseHandle(mainThread);
|
|
||||||
FreeLibraryAndExitThread(hModule, 0);
|
|
||||||
}, nullptr, 0, &mainThreadId);
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
namespace peak
|
|
||||||
{
|
|
||||||
class Memory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
static T pointerToObject(uintptr_t pointer, std::vector<unsigned int> offsets)
|
|
||||||
{
|
|
||||||
HANDLE process = GetCurrentProcess();
|
|
||||||
uintptr_t address = (uintptr_t)GetModuleHandle(L"UnityPlayer.dll") + pointer;
|
|
||||||
for (int i = 0; i < offsets.size(); i++)
|
|
||||||
{
|
|
||||||
ReadProcessMemory(process, (BYTE*)address, &address, sizeof(address), 0);
|
|
||||||
address += offsets[i];
|
|
||||||
}
|
|
||||||
return (T)address;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
struct CharacterData
|
|
||||||
{
|
|
||||||
char _pad0[0x208];
|
|
||||||
float extraStamina;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Character
|
|
||||||
{
|
|
||||||
char _pad0[0xCB];
|
|
||||||
bool infiniteStamina;
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#include "pointers.hpp"
|
|
||||||
|
|
||||||
namespace peak
|
|
||||||
{
|
|
||||||
Pointers::Pointers()
|
|
||||||
{
|
|
||||||
character = Memory::pointerToObject<Character*>(0x01EB35D0, {0x430, 0x9B0, 0x10, 0xC8, 0x8, 0x110, 0x0});
|
|
||||||
pointers = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pointers::~Pointers()
|
|
||||||
{
|
|
||||||
pointers = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#include "memory.hpp"
|
|
||||||
#include "offsets.hpp"
|
|
||||||
|
|
||||||
namespace peak
|
|
||||||
{
|
|
||||||
class Pointers
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Pointers();
|
|
||||||
~Pointers();
|
|
||||||
Character* character{};
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Pointers* pointers{};
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user