<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2019-2022 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "d3d12_test.h"

RD_TEST(D3D12_VRS, D3D12GraphicsTest)
{
  static constexpr const char *Description =
      "Checks that VRS is correctly replayed and that state is inspectable";

  std::string pixel = R"EOSHADER(

uint wang_hash(uint seed)
{
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}

float4 main(float4 pos : SV_Position) : SV_Target0
{
  uint col = wang_hash(uint(pos.x * 10000.0f + pos.y));
  float4 outcol;
  outcol.x = float((col & 0xff000000u) >> 24u) / 255.0f;
  outcol.y = float((col & 0x00ff0000u) >> 16u) / 255.0f;
  outcol.z = float((col & 0x0000ff00u) >>  8u) / 255.0f;
  outcol.w = 1.0f;
	return outcol;
}

)EOSHADER";

  std::string vertex = R"EOSHADER(

struct OUT
{
float4 pos : SV_Position;

#ifdef VERT_VRS
uint rate : SV_ShadingRate;
#endif
};

OUT main(float3 pos : POSITION, float4 col : COLOR0)
{
	OUT o = (OUT)0;

	o.pos = float4(pos.xyz, 1);

#ifdef VERT_VRS
  o.rate = uint(col.x) << 2 | uint(col.y);
#endif

	return o;
}

)EOSHADER";

  void Prepare(int argc, char **argv)
  {
    D3D12GraphicsTest::Prepare(argc, argv);

    if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
      Avail = "Variable shading rate is not supported";
  }

  int main()
  {
    // initialise, create window, create device, etc
    if(!Init())
      return 3;

    ID3DBlobPtr vsblob = Compile(vertex, "main", "vs_5_0");
    ID3DBlobPtr psblob = Compile(pixel, "main", "ps_5_0");

    ID3D12RootSignaturePtr sig = MakeSig({});

    ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob);

    ID3D12PipelineStatePtr vertpso;
    // without DXIL we can't compile shaders with shading rate exported from the vertex
    if(m_DXILSupport)
    {
      vsblob = Compile("#define VERT_VRS 1\n\n" + vertex, "main", "vs_6_4");
      psblob = Compile(pixel, "main", "ps_6_0");

      vertpso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob);
    }

    const DefaultA2V tris[6] = {
        {Vec3f(-1.0f, -0.6f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(-0.5f, 0.4f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
        {Vec3f(0.0f, -0.6f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},

        {Vec3f(0.0f, -0.4f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(0.5f, 0.6f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
        {Vec3f(1.0f, -0.4f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},
    };

    ID3D12ResourcePtr vb = MakeBuffer().Data(tris);

    ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);

    ID3D12ResourcePtr shadImage =
        MakeTexture(DXGI_FORMAT_R8_UINT, screenWidth / opts6.ShadingRateImageTileSize,
                    screenHeight / opts6.ShadingRateImageTileSize)
            .Mips(1)
            .UAV()
            .InitialState(D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    while(Running())
    {
      ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();

      Reset(cmd);

      ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);

      D3D12_CPU_DESCRIPTOR_HANDLE rtv =
          MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0);

      cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr());

      ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f});

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        D3D12_RECT rect = {0, 0, LONG(screenWidth / opts6.ShadingRateImageTileSize),
                           LONG(screenHeight / opts6.ShadingRateImageTileSize)};
        uint32_t col[4] = {};
        col[0] = D3D12_SHADING_RATE_2X2;

        D3D12_CPU_DESCRIPTOR_HANDLE shadCPU = MakeUAV(shadImage).CreateClearCPU(1);
        D3D12_GPU_DESCRIPTOR_HANDLE shadGPU = MakeUAV(shadImage).CreateGPU(1);
        cmd->ClearUnorderedAccessViewUint(shadGPU, shadCPU, shadImage, col, 1, &rect);

        col[0] = D3D12_SHADING_RATE_1X1;
        rect.left = LONG(screenWidth / opts6.ShadingRateImageTileSize) -
                    LONG((screenWidth / 8) / opts6.ShadingRateImageTileSize);
        cmd->ClearUnorderedAccessViewUint(shadGPU, shadCPU, shadImage, col, 1, &rect);

        ResourceBarrier(cmd, shadImage, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
                        D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE);
      }

      cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

      IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0);
      cmd->SetGraphicsRootSignature(sig);

      OMSetRenderTargets(cmd, {rtv}, {});
      RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight});

      float x = (float)screenWidth / 4.0f;
      float y = (float)screenHeight / 4.0f;

      cmd->SetPipelineState(pso);

      ID3D12GraphicsCommandList5Ptr cmd5 = cmd;

      D3D12_SHADING_RATE_COMBINER combiners[] = {
          D3D12_SHADING_RATE_COMBINER_MAX, D3D12_SHADING_RATE_COMBINER_MAX,
      };

      pushMarker(cmd, "First");

      {
        setMarker(cmd, "Default");

        RSSetViewport(cmd, {x * 0.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);
      }

      {
        setMarker(cmd, "Base");

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_2X2, combiners);

        RSSetViewport(cmd, {x * 1.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners);
      }

      if(vertpso)
      {
        setMarker(cmd, "Vertex");

        cmd->SetPipelineState(vertpso);

        RSSetViewport(cmd, {x * 2.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd->SetPipelineState(pso);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmd, "Image");

        cmd5->RSSetShadingRateImage(shadImage);

        RSSetViewport(cmd, {x * 3.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd5->RSSetShadingRateImage(NULL);
      }

      if(vertpso)
      {
        setMarker(cmd, "Base + Vertex");

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_2X2, combiners);
        cmd->SetPipelineState(vertpso);

        RSSetViewport(cmd, {x * 0.0f, y, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd->SetPipelineState(pso);
        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmd, "Base + Image");

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_2X2, combiners);
        cmd5->RSSetShadingRateImage(shadImage);

        RSSetViewport(cmd, {x * 3.0f, y, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd5->RSSetShadingRateImage(NULL);
        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners);
      }

      if(vertpso && opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmd, "Vertex + Image");

        cmd5->RSSetShadingRateImage(shadImage);
        cmd->SetPipelineState(vertpso);

        RSSetViewport(cmd, {x * 3.0f, y * 2.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd->SetPipelineState(pso);
        cmd5->RSSetShadingRateImage(NULL);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        ResourceBarrier(cmd, shadImage, D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE,
                        D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
      }

      popMarker(cmd);

      cmd->Close();
      cmd5 = NULL;

      ID3D12GraphicsCommandListPtr cmdB = GetCommandBuffer();

      Reset(cmdB);

      cmdB->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

      cmdB->SetGraphicsRootSignature(sig);
      cmdB->SetPipelineState(pso);

      OMSetRenderTargets(cmdB, {rtv}, {});
      RSSetScissorRect(cmdB, {0, 0, screenWidth, screenHeight});
      RSSetViewport(cmdB, {0.0f, 0.0f, x, y, 0.0f, 1.0f});

      pushMarker(cmdB, "Second");

      {
        setMarker(cmdB, "Default");

        RSSetViewport(cmdB, {x * 0.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(6, 1, 0, 0);
      }

      {
        setMarker(cmdB, "Base");

        RSSetViewport(cmdB, {x * 1.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      if(vertpso)
      {
        setMarker(cmdB, "Vertex");

        RSSetViewport(cmdB, {x * 2.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmdB, "Image");

        RSSetViewport(cmdB, {x * 3.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      if(vertpso)
      {
        setMarker(cmdB, "Base + Vertex");

        RSSetViewport(cmdB, {x * 0.0f, y, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmdB, "Base + Image");

        RSSetViewport(cmdB, {x * 3.0f, y, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      if(vertpso && opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmdB, "Vertex + Image");

        RSSetViewport(cmdB, {x * 3.0f, y * 2.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      popMarker(cmdB);

      FinishUsingBackbuffer(cmdB, D3D12_RESOURCE_STATE_RENDER_TARGET);

      cmdB->Close();

      Submit({cmd, cmdB});

      Present();
    }

    return 0;
  }
};

REGISTER_TEST();
<commit_msg>Handle D3D12 tier 1 VRS devices better when testing<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2019-2022 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "d3d12_test.h"

RD_TEST(D3D12_VRS, D3D12GraphicsTest)
{
  static constexpr const char *Description =
      "Checks that VRS is correctly replayed and that state is inspectable";

  std::string pixel = R"EOSHADER(

uint wang_hash(uint seed)
{
    seed = (seed ^ 61) ^ (seed >> 16);
    seed *= 9;
    seed = seed ^ (seed >> 4);
    seed *= 0x27d4eb2d;
    seed = seed ^ (seed >> 15);
    return seed;
}

float4 main(float4 pos : SV_Position) : SV_Target0
{
  uint col = wang_hash(uint(pos.x * 10000.0f + pos.y));
  float4 outcol;
  outcol.x = float((col & 0xff000000u) >> 24u) / 255.0f;
  outcol.y = float((col & 0x00ff0000u) >> 16u) / 255.0f;
  outcol.z = float((col & 0x0000ff00u) >>  8u) / 255.0f;
  outcol.w = 1.0f;
	return outcol;
}

)EOSHADER";

  std::string vertex = R"EOSHADER(

struct OUT
{
float4 pos : SV_Position;

#ifdef VERT_VRS
uint rate : SV_ShadingRate;
#endif
};

OUT main(float3 pos : POSITION, float4 col : COLOR0)
{
	OUT o = (OUT)0;

	o.pos = float4(pos.xyz, 1);

#ifdef VERT_VRS
  o.rate = uint(col.x) << 2 | uint(col.y);
#endif

	return o;
}

)EOSHADER";

  void Prepare(int argc, char **argv)
  {
    D3D12GraphicsTest::Prepare(argc, argv);

    if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
      Avail = "Variable shading rate is not supported";
  }

  int main()
  {
    // initialise, create window, create device, etc
    if(!Init())
      return 3;

    ID3DBlobPtr vsblob = Compile(vertex, "main", "vs_5_0");
    ID3DBlobPtr psblob = Compile(pixel, "main", "ps_5_0");

    ID3D12RootSignaturePtr sig = MakeSig({});

    ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob);

    ID3D12PipelineStatePtr vertpso;
    // without DXIL we can't compile shaders with shading rate exported from the vertex
    if(m_DXILSupport)
    {
      vsblob = Compile("#define VERT_VRS 1\n\n" + vertex, "main", "vs_6_4");
      psblob = Compile(pixel, "main", "ps_6_0");

      vertpso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob);
    }

    const DefaultA2V tris[6] = {
        {Vec3f(-1.0f, -0.6f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(-0.5f, 0.4f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
        {Vec3f(0.0f, -0.6f, 0.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},

        {Vec3f(0.0f, -0.4f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 0.0f)},
        {Vec3f(0.5f, 0.6f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(0.0f, 1.0f)},
        {Vec3f(1.0f, -0.4f, 0.0f), Vec4f(1.0f, 1.0f, 0.0f, 1.0f), Vec2f(1.0f, 0.0f)},
    };

    ID3D12ResourcePtr vb = MakeBuffer().Data(tris);

    ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);

    if(opts6.ShadingRateImageTileSize == 0)
      opts6.ShadingRateImageTileSize = 1;

    ID3D12ResourcePtr shadImage =
        MakeTexture(DXGI_FORMAT_R8_UINT, screenWidth / opts6.ShadingRateImageTileSize,
                    screenHeight / opts6.ShadingRateImageTileSize)
            .Mips(1)
            .UAV()
            .InitialState(D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    while(Running())
    {
      ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();

      Reset(cmd);

      ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);

      D3D12_CPU_DESCRIPTOR_HANDLE rtv =
          MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0);

      cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr());

      ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f});

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        D3D12_RECT rect = {0, 0, LONG(screenWidth / opts6.ShadingRateImageTileSize),
                           LONG(screenHeight / opts6.ShadingRateImageTileSize)};
        uint32_t col[4] = {};
        col[0] = D3D12_SHADING_RATE_2X2;

        D3D12_CPU_DESCRIPTOR_HANDLE shadCPU = MakeUAV(shadImage).CreateClearCPU(1);
        D3D12_GPU_DESCRIPTOR_HANDLE shadGPU = MakeUAV(shadImage).CreateGPU(1);
        cmd->ClearUnorderedAccessViewUint(shadGPU, shadCPU, shadImage, col, 1, &rect);

        col[0] = D3D12_SHADING_RATE_1X1;
        rect.left = LONG(screenWidth / opts6.ShadingRateImageTileSize) -
                    LONG((screenWidth / 8) / opts6.ShadingRateImageTileSize);
        cmd->ClearUnorderedAccessViewUint(shadGPU, shadCPU, shadImage, col, 1, &rect);

        ResourceBarrier(cmd, shadImage, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
                        D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE);
      }

      cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

      IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0);
      cmd->SetGraphicsRootSignature(sig);

      OMSetRenderTargets(cmd, {rtv}, {});
      RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight});

      float x = (float)screenWidth / 4.0f;
      float y = (float)screenHeight / 4.0f;

      cmd->SetPipelineState(pso);

      ID3D12GraphicsCommandList5Ptr cmd5 = cmd;

      D3D12_SHADING_RATE_COMBINER combiners[] = {
          D3D12_SHADING_RATE_COMBINER_MAX, D3D12_SHADING_RATE_COMBINER_MAX,
      };

      pushMarker(cmd, "First");

      {
        setMarker(cmd, "Default");

        RSSetViewport(cmd, {x * 0.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);
      }

      {
        setMarker(cmd, "Base");

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_2X2, combiners);

        RSSetViewport(cmd, {x * 1.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmd, "Vertex");

        cmd->SetPipelineState(vertpso);

        RSSetViewport(cmd, {x * 2.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd->SetPipelineState(pso);
        setMarker(cmd, "Image");

        cmd5->RSSetShadingRateImage(shadImage);

        RSSetViewport(cmd, {x * 3.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd5->RSSetShadingRateImage(NULL);

        setMarker(cmd, "Base + Vertex");

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_2X2, combiners);
        cmd->SetPipelineState(vertpso);

        RSSetViewport(cmd, {x * 0.0f, y, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd->SetPipelineState(pso);
        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners);

        setMarker(cmd, "Base + Image");

        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_2X2, combiners);
        cmd5->RSSetShadingRateImage(shadImage);

        RSSetViewport(cmd, {x * 3.0f, y, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd5->RSSetShadingRateImage(NULL);
        cmd5->RSSetShadingRate(D3D12_SHADING_RATE_1X1, combiners);

        setMarker(cmd, "Vertex + Image");

        cmd5->RSSetShadingRateImage(shadImage);
        cmd->SetPipelineState(vertpso);

        RSSetViewport(cmd, {x * 3.0f, y * 2.0f, x, y, 0.0f, 1.0f});
        cmd->DrawInstanced(6, 1, 0, 0);

        cmd->SetPipelineState(pso);
        cmd5->RSSetShadingRateImage(NULL);

        ResourceBarrier(cmd, shadImage, D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE,
                        D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
      }

      popMarker(cmd);

      cmd->Close();
      cmd5 = NULL;

      ID3D12GraphicsCommandListPtr cmdB = GetCommandBuffer();

      Reset(cmdB);

      cmdB->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

      cmdB->SetGraphicsRootSignature(sig);
      cmdB->SetPipelineState(pso);

      OMSetRenderTargets(cmdB, {rtv}, {});
      RSSetScissorRect(cmdB, {0, 0, screenWidth, screenHeight});
      RSSetViewport(cmdB, {0.0f, 0.0f, x, y, 0.0f, 1.0f});

      pushMarker(cmdB, "Second");

      {
        setMarker(cmdB, "Default");

        RSSetViewport(cmdB, {x * 0.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(6, 1, 0, 0);
      }

      {
        setMarker(cmdB, "Base");

        RSSetViewport(cmdB, {x * 1.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      if(opts6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2)
      {
        setMarker(cmdB, "Vertex");

        RSSetViewport(cmdB, {x * 2.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);

        setMarker(cmdB, "Image");

        RSSetViewport(cmdB, {x * 3.0f, 0.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);

        setMarker(cmdB, "Base + Vertex");

        RSSetViewport(cmdB, {x * 0.0f, y, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);

        setMarker(cmdB, "Base + Image");

        RSSetViewport(cmdB, {x * 3.0f, y, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);

        setMarker(cmdB, "Vertex + Image");

        RSSetViewport(cmdB, {x * 3.0f, y * 2.0f, x, y, 0.0f, 1.0f});
        cmdB->DrawInstanced(0, 0, 0, 0);
      }

      popMarker(cmdB);

      FinishUsingBackbuffer(cmdB, D3D12_RESOURCE_STATE_RENDER_TARGET);

      cmdB->Close();

      Submit({cmd, cmdB});

      Present();
    }

    return 0;
  }
};

REGISTER_TEST();
<|endoftext|>


<commit_before>//
// Created by mathias on 6/1/16.
//

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Empty.h"
#include "geometry_msgs/Twist.h"
#include "Route.h"
#include "FlightController.h"

#define LOOP_RATE (50)

struct thread_data{
    ros::Publisher pub_land;
    ros::Publisher pub_takeoff;
    ros::Publisher pub_control;
    FlightController controller;
    Route myRoute;
};

void *controlThread(void *thread_arg);
void *abortThread(void *thread_arg);

int main(int argc, char **argv) {
    struct thread_data td[NUM_THREADS];

    ros::init(argc, argv, "blindFlight");

    ros::NodeHandle n;

    td[0].controller = FlightController(LOOP_RATE, n);
    td[0].myRoute = Route();
    td[1].pub_land = pub_land;

    pthread_t threads[NUM_THREADS];
    pthread_create(&threads[0], NULL, controlThread, &td[0]);
    pthread_create(&threads[1], NULL, abortThread, &td[1]);

    ros::spin();

    pthread_exit(NULL);
}

void *controlThread(void *thread_arg) {
    int takeoff_time = 3;
    double fly_time = 1.0;
    double land_time = 3.0;
    struct thread_data *thread_data;
    thread_data = (struct thread_data *) thread_arg;

    thread_data->controller.setStraightFlight(true);

    int i = 0;
    printf("Enter a key to start: ");
    getchar();
    while (ros::ok()) {

        thread_data->controller.takeOff();

        while (!thread_data->myRoute.hasAllBeenVisited()) {
            Command currentCommand = thread_data->myRoute.nextCommand();

            if (currentCommand.commandType == Command::goTo) {
                controller.goToWaypoint(currentCommand);
            } else if (currentCommand.commandType == Command::hover) {
                controller.hover(currentCommand.timeToHover);
            } else if (currentCommand.commandType == Command::turn) {
                controller.turnDrone(currentCommand.degrees);
            }
        }

        thread_data->controller.land();

        pthread_exit(NULL);

    }

    pthread_exit(NULL);
}

void *abortThread(void *thread_arg) {
    struct thread_data *thread_data;
    thread_data = (struct thread_data*) thread_arg;

    // System call to make terminal send all keystrokes directly to stdin
    system("/bin/stty raw");

    while (1) {
        // Abort if 'Esc' is pressed
        if  (getchar() == 27) {
            ROS_INFO("MANUEL ABORT!");
            std_msgs::Empty empty_msg;
            thread_data->pub_land.publish(empty_msg);
            break;
        }   usleep(10);
    }

    // System call to set terminal behaviour to normal
    system("/bin/stty cooked");

    pthread_exit(NULL);
}<commit_msg>fixed merge bugs<commit_after>//
// Created by mathias on 6/1/16.
//

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Empty.h"
#include "geometry_msgs/Twist.h"
#include "Route.h"
#include "FlightController.h"

#define NUM_THREADS 2
#define LOOP_RATE (50)

struct thread_data{
    ros::Publisher pub_land;
    ros::Publisher pub_takeoff;
    ros::Publisher pub_control;
    FlightController controller;
    Route myRoute;
};

void *controlThread(void *thread_arg);
void *abortThread(void *thread_arg);

int main(int argc, char **argv) {
    struct thread_data td[NUM_THREADS];

    ros::init(argc, argv, "blindFlight");

    ros::NodeHandle n;

    FlightController controller(LOOP_RATE, n);

    td[0].controller = controller;
    td[0].myRoute = Route();
    td[1].controller = controller;

    pthread_t threads[NUM_THREADS];
    pthread_create(&threads[0], NULL, controlThread, &td[0]);
    pthread_create(&threads[1], NULL, abortThread, &td[1]);

    ros::spin();

    pthread_exit(NULL);
}

void *controlThread(void *thread_arg) {
    struct thread_data *thread_data;
    thread_data = (struct thread_data *) thread_arg;

    thread_data->controller.setStraightFlight(true);

    while (ros::ok()) {

        printf("Enter a key to start: ");
        getchar();

        thread_data->controller.takeOff();

        while (!thread_data->myRoute.hasAllBeenVisited()) {
            Command currentCommand = thread_data->myRoute.nextCommand();

            if (currentCommand.commandType == Command::goTo) {
                thread_data->controller.goToWaypoint(currentCommand);
            } else if (currentCommand.commandType == Command::hover) {
                thread_data->controller.hover(currentCommand.timeToHover);
            } else if (currentCommand.commandType == Command::turn) {
                thread_data->controller.turnDrone(currentCommand.degrees);
            }
        }

        thread_data->controller.land();

        pthread_exit(NULL);

    }

    pthread_exit(NULL);
}

void *abortThread(void *thread_arg) {
    struct thread_data *thread_data;
    thread_data = (struct thread_data*) thread_arg;

    // System call to make terminal send all keystrokes directly to stdin
    system("/bin/stty raw");

    while (1) {
        // Abort if 'Esc' is pressed
        if  (getchar() == 27) {
            ROS_INFO("MANUEL ABORT!");
            thread_data->controller.land();
            break;
        }   usleep(10);
    }

    // System call to set terminal behaviour to normal
    system("/bin/stty cooked");

    pthread_exit(NULL);
}<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: olewrapclient.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 18:44:25 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <osl/interlck.h>
#include <osl/mutex.hxx>
#include <platform.h>

class OleComponent;
class OleWrapperClientSite : public IOleClientSite
{
    osl::Mutex m_aMutex;
    oslInterlockedCount m_nRefCount;
    OleComponent* m_pOleComp;

public:
    OleWrapperClientSite( OleComponent* pOleComp );
    ~OleWrapperClientSite(void);

    void disconnectOleComponent();

    STDMETHODIMP QueryInterface(REFIID, void**);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    STDMETHODIMP SaveObject(void);
    STDMETHODIMP GetMoniker(DWORD, DWORD, LPMONIKER *);
    STDMETHODIMP GetContainer(LPOLECONTAINER *);
    STDMETHODIMP ShowObject(void);
    STDMETHODIMP OnShowWindow(BOOL);
    STDMETHODIMP RequestNewObjectLayout(void);
};

<commit_msg>INTEGRATION: CWS sb59 (1.2.64); FILE MERGED 2006/08/29 14:08:21 sb 1.2.64.1: #i67487# Made code warning-free (wntmsci10).<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: olewrapclient.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: obo $ $Date: 2006-10-12 11:23:42 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <osl/interlck.h>
#include <osl/mutex.hxx>
#include <platform.h>

class OleComponent;
class OleWrapperClientSite : public IOleClientSite
{
    osl::Mutex m_aMutex;
    oslInterlockedCount m_nRefCount;
    OleComponent* m_pOleComp;

public:
    OleWrapperClientSite( OleComponent* pOleComp );
    virtual ~OleWrapperClientSite(void);

    void disconnectOleComponent();

    STDMETHODIMP QueryInterface(REFIID, void**);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    STDMETHODIMP SaveObject(void);
    STDMETHODIMP GetMoniker(DWORD, DWORD, LPMONIKER *);
    STDMETHODIMP GetContainer(LPOLECONTAINER *);
    STDMETHODIMP ShowObject(void);
    STDMETHODIMP OnShowWindow(BOOL);
    STDMETHODIMP RequestNewObjectLayout(void);
};

<|endoftext|>


<commit_before>
// $Id: Cache.cpp 3456 2013-06-14 02:11:13Z jiaying $

/*
 * The Software is made available solely for use according to the License Agreement. Any reproduction
 * or redistribution of the Software not in accordance with the License Agreement is expressly prohibited
 * by law, and may result in severe civil and criminal penalties. Violators will be prosecuted to the
 * maximum extent possible.
 *
 * THE SOFTWARE IS WARRANTED, IF AT ALL, ONLY ACCORDING TO THE TERMS OF THE LICENSE AGREEMENT. EXCEPT
 * AS WARRANTED IN THE LICENSE AGREEMENT, SRCH2 INC. HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS WITH
 * REGARD TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT.  IN NO EVENT SHALL SRCH2 INC. BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF SOFTWARE.

 * Copyright © 2010 SRCH2 Inc. All rights reserved
 */

#include "CacheManager.h"
#include "util/Assert.h"
#include "util/Logger.h"
#include "operation/physical_plan/PhysicalPlan.h"
#include <string>
#include <map>
#include <stddef.h>

#include <iostream>
#include <sstream>

using std::vector;
using std::map;

namespace srch2
{
namespace instantsearch
{

bool PhysicalOperatorsCache::getPhysicalOperatorsInfo(string & key,  boost::shared_ptr<PhysicalOperatorCacheObject> & in){
	return this->cacheContainer->get(key , in);
}
void PhysicalOperatorsCache::setPhysicalOperatosInfo(string & key , boost::shared_ptr<PhysicalOperatorCacheObject> object){
	this->cacheContainer->put(key , object);
}
int PhysicalOperatorsCache::clear(){
	return this->cacheContainer->clear();
}

int ActiveNodesCache::findLongestPrefixActiveNodes(Term *term, boost::shared_ptr<PrefixActiveNodeSet> &in){

	// find the longest prefix with active nodes in the cache
	unsigned termThreshold = term->getThreshold();
	string *keyword = term->getKeyword();
	for (int i = keyword->size(); i >= 2; i --)
	{
		string prefix = keyword->substr(0, i);
		std::string exactOrFuzzy =  termThreshold == 0?"0":"1";
		string key = prefix + "$" + exactOrFuzzy;
		// Cache key is : keyword+0 (for exact) or keyword+1 (for fuzzy)
		// for example: terminator => "terminator$0"
		//         and  terminator~0.5 => "terminator$1"
		boost::shared_ptr<PrefixActiveNodeSet> cacheHit;
		if(this->cacheContainer->get(key , cacheHit) == true && cacheHit->getEditDistanceThreshold() >= termThreshold){
			in = cacheHit;
			return 1;
		}
	}
    // no prefix has a cached PrefixActiveNodeSet
	return 0;

}

int ActiveNodesCache::setPrefixActiveNodeSet(boost::shared_ptr<PrefixActiveNodeSet> &prefixActiveNodeSet){
	vector<CharType> *prefix = prefixActiveNodeSet->getPrefix();
	std::stringstream ss ;
	ss << prefixActiveNodeSet->getEditDistanceThreshold();
	std::string exactOrFuzzy = ss.str();
//	std::string exactOrFuzzy =  prefixActiveNodeSet->getEditDistanceThreshold() == 0?"0":"1";
	string key = getUtf8String(*prefix) + "$" + exactOrFuzzy;
	this->cacheContainer->put(key , prefixActiveNodeSet);
	return 1;
}
int ActiveNodesCache::clear(){
	return this->cacheContainer->clear();
}

ActiveNodesCache * CacheManager::getActiveNodesCache(){
	return this->aCache;
}

QueryResultsCache * CacheManager::getQueryResultsCache(){
	return this->qCache;
}

PhysicalOperatorsCache * CacheManager::getPhysicalOperatorsCache(){
	return this->pCache;
}

PhysicalPlanRecordItemFactory * CacheManager::getPhysicalPlanRecordItemFactory(){
	return this->physicalPlanRecordItemFactory;
}

bool QueryResultsCache::getQueryResults(string & key, boost::shared_ptr<QueryResultsCacheEntry> & in){
	return this->cacheContainer->get(key , in);
}
void QueryResultsCache::setQueryResults(string & key , boost::shared_ptr<QueryResultsCacheEntry> object){
	this->cacheContainer->put(key , object);
}
int QueryResultsCache::clear(){
	return this->cacheContainer->clear();
}

int CacheManager::clear(){
	return this->aCache->clear() && this->qCache->clear() && this->pCache->clear() && this->physicalPlanRecordItemFactory->clear();
}

}}
<commit_msg>disabled active node caching temporarily<commit_after>
// $Id: Cache.cpp 3456 2013-06-14 02:11:13Z jiaying $

/*
 * The Software is made available solely for use according to the License Agreement. Any reproduction
 * or redistribution of the Software not in accordance with the License Agreement is expressly prohibited
 * by law, and may result in severe civil and criminal penalties. Violators will be prosecuted to the
 * maximum extent possible.
 *
 * THE SOFTWARE IS WARRANTED, IF AT ALL, ONLY ACCORDING TO THE TERMS OF THE LICENSE AGREEMENT. EXCEPT
 * AS WARRANTED IN THE LICENSE AGREEMENT, SRCH2 INC. HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS WITH
 * REGARD TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT.  IN NO EVENT SHALL SRCH2 INC. BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF SOFTWARE.

 * Copyright © 2010 SRCH2 Inc. All rights reserved
 */

#include "CacheManager.h"
#include "util/Assert.h"
#include "util/Logger.h"
#include "operation/physical_plan/PhysicalPlan.h"
#include <string>
#include <map>
#include <stddef.h>

#include <iostream>
#include <sstream>

using std::vector;
using std::map;

namespace srch2
{
namespace instantsearch
{

bool PhysicalOperatorsCache::getPhysicalOperatorsInfo(string & key,  boost::shared_ptr<PhysicalOperatorCacheObject> & in){
	return this->cacheContainer->get(key , in);
}
void PhysicalOperatorsCache::setPhysicalOperatosInfo(string & key , boost::shared_ptr<PhysicalOperatorCacheObject> object){
	this->cacheContainer->put(key , object);
}
int PhysicalOperatorsCache::clear(){
	return this->cacheContainer->clear();
}

int ActiveNodesCache::findLongestPrefixActiveNodes(Term *term, boost::shared_ptr<PrefixActiveNodeSet> &in){
  // TODO: Disable cachinng temporarily
  return 0;

	// find the longest prefix with active nodes in the cache
	unsigned termThreshold = term->getThreshold();
	string *keyword = term->getKeyword();
	for (int i = keyword->size(); i >= 2; i --)
	{
		string prefix = keyword->substr(0, i);
		std::string exactOrFuzzy =  termThreshold == 0?"0":"1";
		string key = prefix + "$" + exactOrFuzzy;
		// Cache key is : keyword+0 (for exact) or keyword+1 (for fuzzy)
		// for example: terminator => "terminator$0"
		//         and  terminator~0.5 => "terminator$1"
		boost::shared_ptr<PrefixActiveNodeSet> cacheHit;
		if(this->cacheContainer->get(key , cacheHit) == true && cacheHit->getEditDistanceThreshold() >= termThreshold){
			in = cacheHit;
			return 1;
		}
	}
    // no prefix has a cached PrefixActiveNodeSet
	return 0;

}

int ActiveNodesCache::setPrefixActiveNodeSet(boost::shared_ptr<PrefixActiveNodeSet> &prefixActiveNodeSet){
  // TODO: disable caching temporarily
  return 1;

	vector<CharType> *prefix = prefixActiveNodeSet->getPrefix();
	std::stringstream ss ;
	ss << prefixActiveNodeSet->getEditDistanceThreshold();
	std::string exactOrFuzzy = ss.str();
//	std::string exactOrFuzzy =  prefixActiveNodeSet->getEditDistanceThreshold() == 0?"0":"1";
	string key = getUtf8String(*prefix) + "$" + exactOrFuzzy;
	this->cacheContainer->put(key , prefixActiveNodeSet);
	return 1;
}
int ActiveNodesCache::clear(){
	return this->cacheContainer->clear();
}

ActiveNodesCache * CacheManager::getActiveNodesCache(){
	return this->aCache;
}

QueryResultsCache * CacheManager::getQueryResultsCache(){
	return this->qCache;
}

PhysicalOperatorsCache * CacheManager::getPhysicalOperatorsCache(){
	return this->pCache;
}

PhysicalPlanRecordItemFactory * CacheManager::getPhysicalPlanRecordItemFactory(){
	return this->physicalPlanRecordItemFactory;
}

bool QueryResultsCache::getQueryResults(string & key, boost::shared_ptr<QueryResultsCacheEntry> & in){
	return this->cacheContainer->get(key , in);
}
void QueryResultsCache::setQueryResults(string & key , boost::shared_ptr<QueryResultsCacheEntry> object){
	this->cacheContainer->put(key , object);
}
int QueryResultsCache::clear(){
	return this->cacheContainer->clear();
}

int CacheManager::clear(){
	return this->aCache->clear() && this->qCache->clear() && this->pCache->clear() && this->physicalPlanRecordItemFactory->clear();
}

}}
<|endoftext|>


<commit_before>/** \file   system_monitor_viewer.cc
 *  \brief  Visualises the metrics collected by the system_monitor service
 *  \author Madeeswaran Kannan (madeeswaran.kannan@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <unordered_map>
#include <ctime>
#include "ExecUtil.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "MiscUtil.h"
#include "StringUtil.h"
#include "TextUtil.h"
#include "TimeUtil.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--output-filename=path] system_id metric time_range\n"
            "       system_id       - One of the following: nu ptah sobek ub15 ub16 ub28\n"
            "       metric          - One of the following: mem cpu disk\n"
            "       time_range      - One of the following time ranges:\n"
            "                            YYYY/MM/DD[THH:MM:SS][-YYYY/MM/DD[THH:MM:SS]\n"
            "                            last <n> <hours|days|weeks|months>\n"
            "       The config file path is \"" + UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf\".");
}

bool ParseTimestamp(const std::string &timestamp, struct tm * const tm) {
    return TimeUtil::StringToStructTm(tm, timestamp, "%Y/%m/%d") or
           TimeUtil::StringToStructTm(tm, timestamp, "%Y/%m/%dT%H:%M:%S");
}

void ParseTimeRange(const std::string &range_string, time_t * const time_start, time_t * const time_end) {
    std::vector<std::string> tokens;

    if (StringUtil::StartsWith(range_string, "last ", true)) {
        StringUtil::SplitThenTrimWhite(range_string, ' ', &tokens, true);
        if (tokens.size() != 3)
            LOG_ERROR("invalid time range");

        unsigned time_atom(0);
        if (not StringUtil::ToUnsigned(tokens[1], &time_atom))
            LOG_ERROR("time atom has to be greater than zero");

        auto current_time(std::time(nullptr));
        unsigned long seconds_to_deduct(0);
        const auto granularity(tokens[2]);
        if (StringUtil::StartsWith(granularity, "hour", true))
            seconds_to_deduct = time_atom * 3600;
        else if (StringUtil::StartsWith(granularity, "day", true))
            seconds_to_deduct = time_atom * 24 * 3600;
        else if (StringUtil::StartsWith(granularity, "week", true))
            seconds_to_deduct = time_atom * 7 * 24 * 3600;
        else if (StringUtil::StartsWith(granularity, "month", true))
            seconds_to_deduct = time_atom * 31 * 24 * 3600;
        else
            LOG_ERROR("invalid time range");

        *time_start = current_time - seconds_to_deduct;
        *time_end = current_time;

        return;
    }

    struct tm start_time_buffer, end_time_buffer;
    std::memset(&start_time_buffer, 0, sizeof(struct tm));
    std::memset(&end_time_buffer, 0, sizeof(struct tm));

    if (ParseTimestamp(range_string, &start_time_buffer)) {
        *time_start = std::mktime(&start_time_buffer);
        *time_end = TimeUtil::BAD_TIME_T;
        if (*time_start == TimeUtil::BAD_TIME_T)
            LOG_ERROR("invalid time range");

        return;
    }

    if (StringUtil::Split(range_string, '-', &tokens, true) != 2)
        LOG_ERROR("invalid time range");

    if (not ParseTimestamp(tokens[0], &start_time_buffer) or
        not ParseTimestamp(tokens[1], &end_time_buffer))
    {
        LOG_ERROR("invalid time range");
    }

    *time_start = std::mktime(&start_time_buffer);
    *time_end = std::mktime(&end_time_buffer);
    if (*time_start == TimeUtil::BAD_TIME_T)
        LOG_ERROR("invalid time range");
}

struct Datapoint {
    std::string label_;
    time_t timestamp_;
    std::string value_;

    Datapoint(const std::string &label, const time_t timestamp, const std::string &value)
        : label_(label), timestamp_(timestamp), value_(value) {}

    bool operator<(const Datapoint &rhs) const {
        return this->timestamp_ < rhs.timestamp_;
    }
};

extern const std::unordered_map<std::string, std::string> INDIVIDUAL_METRIC_TO_LABEL_MAP;

void LoadSystemMonitorLog(const std::string &log_path, std::vector<Datapoint> * const data) {
    static constexpr size_t DATA_INITIAL_SIZE = 1000 * 1000;

    File log_file(log_path, "r");
    int line_num(1);
    std::vector<std::string> parts;
    data->reserve(DATA_INITIAL_SIZE);

    while (not log_file.eof()) {
        const auto line(TextUtil::CollapseAndTrimWhitespace(log_file.getline()));
        if (not line.empty()) {
            if (StringUtil::Split(line, ' ', &parts, true) != 3)
                LOG_ERROR("invalid line " + std::to_string(line_num) + " in file '" + log_path + "': " + line);

            const auto metric_name(parts[0]);
            const auto label_match(INDIVIDUAL_METRIC_TO_LABEL_MAP.find(metric_name));
            if (label_match != INDIVIDUAL_METRIC_TO_LABEL_MAP.end()) {
                struct tm tm_buffer;
                std::memset(&tm_buffer, 0, sizeof(struct tm));

                if (not TimeUtil::StringToStructTm(&tm_buffer, parts[2], TimeUtil::ISO_8601_FORMAT))
                    LOG_ERROR("invalid timestamp on line " + std::to_string(line_num));

                data->emplace_back(label_match->second, std::mktime(&tm_buffer), parts[1]);
            } else
                LOG_DEBUG("unknown metric '" + metric_name + "' in line " + std::to_string(line_num));
        }

        ++line_num;
    }

    // sort by timestamp
    std::sort(data->begin(), data->end());
}

void GetDataRange(const time_t time_start, const time_t time_end, const std::vector<Datapoint> &data,
                  std::vector<Datapoint>::const_iterator * const begin, std::vector<Datapoint>::const_iterator * const end)
{
    // The list should be sorted at this point
    *begin = std::lower_bound(data.begin(), data.end(), Datapoint("", time_start, ""));
    *end = std::upper_bound(data.begin(), data.end(), Datapoint("", time_end, ""));
}

unsigned WritePlotDataToDisk(const std::string &output_path, const std::vector<std::string> &labels,
                         const std::vector<Datapoint>::const_iterator &data_begin, const std::vector<Datapoint>::const_iterator &data_end)
{
    // We expect the values of the labels to use the same axis/scale
    // Columns: Timestamp [Label 1..n]
    File plot_data(output_path, "w");

    plot_data.writeln("#\t" + StringUtil::Join(labels, '\t'));

    time_t current_write_timestamp(TimeUtil::BAD_TIME_T);
    std::map<std::string, std::string> current_write_timestamp_values;
    unsigned lines_written(0);

    for (auto itr(data_begin); itr != data_end; ++itr) {
        const auto datapoint(*itr);

        if (current_write_timestamp == datapoint.timestamp_) {
            current_write_timestamp_values[datapoint.label_] = datapoint.value_;
            continue;
        }

        if (not current_write_timestamp_values.empty()) {
            std::string out_line(std::to_string(current_write_timestamp) + "\t");
            for (const auto &label : labels) {
                const auto value(current_write_timestamp_values.find(label));
                if (value != current_write_timestamp_values.end())
                    out_line += value->second + "\t";
                else
                    out_line += "\t";
            }

            plot_data.writeln(out_line);
            ++lines_written;
        }

        current_write_timestamp = datapoint.timestamp_;
        current_write_timestamp_values[datapoint.label_] = datapoint.value_;
    }

    if (not current_write_timestamp_values.empty()) {
        std::string out_line(std::to_string(current_write_timestamp) + "\t");
        for (const auto &label : labels) {
            const auto value(current_write_timestamp_values.find(label));
            if (value != current_write_timestamp_values.end())
                out_line += value->second + "\t";
            else
                out_line += "\t";
        }

        plot_data.writeln(out_line);
        ++lines_written;
    }

    return lines_written;
}

void DisplayPlot(const std::string &data_path, const std::string &script_path, const std::string &plot_path) {
    if (not FileUtil::Exists(data_path))
        LOG_ERROR("data file for plotting does not exist at " + data_path);
    else if (not FileUtil::Exists(script_path))
        LOG_ERROR("script file for plotting does not exist at " + script_path);

    std::vector<std::string> gnuplot_args {
        "-c",
        script_path,
        data_path,
        plot_path,
    };
    ExecUtil::ExecOrDie("/usr/bin/gnuplot", gnuplot_args);

    std::vector<std::string> xdg_args {
        plot_path
    };

    ExecUtil::ExecOrDie("/usr/bin/xdg-open", xdg_args);
}

const std::set<std::string> VALID_SYSTEM_IDS{
    "nu", "ptah", "sobek", "ub15", "ub16", "ub28"
};

const std::set<std::string> VALID_COARSE_METRICS{
    "cpu", "mem", "disk"
};

const std::unordered_map<std::string, std::string> INDIVIDUAL_METRIC_TO_LABEL_MAP {
    { "MemAvailable", "Mem-Free"        },
    { "Unevictable",  "Mem-Unevictable" },
    { "SwapFree",     "Mem-SwapFree"    },
    { "CPU",          "CPU"             }
};

void GetLabelsForCoarseMetric(const std::string &coarse_metric, std::vector<std::string> * const labels) {
    // this order needs to be observed in the plotting scripts
    if (coarse_metric == "mem") {
        labels->emplace_back("Mem-Free");
        labels->emplace_back("Mem-Unevictable");
        labels->emplace_back("Mem-SwapFree");
    } else if (coarse_metric == "cpu")
        labels->emplace_back("Cpu");
    else if (coarse_metric == "disk")
        ;
    else
        LOG_ERROR("invalid coarse metric '" + coarse_metric + "'");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc < 4)
        Usage();

    const std::string OUTPUT_FILENAME_FLAG_PREFIX("--output-filename=");
    std::string output_filename;
    if (StringUtil::StartsWith(argv[1], OUTPUT_FILENAME_FLAG_PREFIX)) {
        output_filename = argv[1] + OUTPUT_FILENAME_FLAG_PREFIX.length();
        --argc, ++argv;
    }

    if (argc != 4)
        Usage();

    const std::string system_id(TextUtil::UTF8ToLower(argv[1]));
    const std::string coarse_metric(TextUtil::UTF8ToLower(argv[2]));
    const std::string time_range(argv[3]);

    if (VALID_SYSTEM_IDS.find(system_id) == VALID_SYSTEM_IDS.end())
        LOG_ERROR("invalid system ID '" + system_id + "'");
    else {
        const auto hostname(MiscUtil::SafeGetEnv("HOSTNAME"));
        if (not StringUtil::StartsWith(hostname, system_id, true))
            LOG_WARNING("attempting to view system monitor data of a system that is not the host. time range may be inaccurate");
    }

    time_t time_start, time_end;
    std::vector<Datapoint> log_data;
    std::vector<Datapoint>::const_iterator data_range_start, data_range_end;
    std::vector<std::string> labels;

    ParseTimeRange(time_range, &time_start, &time_end);
    GetLabelsForCoarseMetric(coarse_metric, &labels);

    const IniFile ini_file(UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf");
    const auto log_file(ini_file.getString("Logs", system_id));
    const auto plot_data_file(ini_file.getString("Default Plotting Inputs", coarse_metric));
    const auto plot_script_file(ini_file.getString("Plotting Scripts", coarse_metric));
    if (output_filename.empty())
        output_filename = ini_file.getString("Default Plotting Outputs", coarse_metric);

    LoadSystemMonitorLog(log_file, &log_data);
    GetDataRange(time_start, time_end, log_data, &data_range_start, &data_range_end);

    if (data_range_start == log_data.end())
        LOG_ERROR("found no data that was newer than the given range's beginning");

    if (time_end == TimeUtil::BAD_TIME_T) {
        // print out the closest data point
        if (data_range_start->timestamp_ == time_start)
            LOG_INFO("Data for exact time point (" + TimeUtil::TimeTToString(time_start) + "):");
        else
            LOG_INFO("Data for closest time point (" + TimeUtil::TimeTToString(time_start) + "):");

        const auto datapoint_timestamp(data_range_start->timestamp_);
        while (data_range_start != log_data.end() and data_range_start->timestamp_ == datapoint_timestamp)
            LOG_INFO("\t" + data_range_start->label_ + " = " + data_range_start->value_);

        return EXIT_SUCCESS;
    }

    if (WritePlotDataToDisk(plot_data_file, labels, data_range_start, data_range_end) == 0)
        LOG_WARNING("found no data for the given time range");
    else
        DisplayPlot(plot_data_file, plot_script_file, output_filename);

    return EXIT_SUCCESS;
}
<commit_msg>system_monitor_viewer: switch to binary log format<commit_after>/** \file   system_monitor_viewer.cc
 *  \brief  Visualises the metrics collected by the system_monitor service
 *  \author Madeeswaran Kannan (madeeswaran.kannan@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <unordered_map>
#include <ctime>
#include "BinaryIO.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "MiscUtil.h"
#include "StringUtil.h"
#include "TextUtil.h"
#include "TimeUtil.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--output-filename=path] system_id metric time_range\n"
            "       system_id       - One of the following: nu ptah sobek ub15 ub16 ub28\n"
            "       metric          - One of the following: mem cpu disk\n"
            "       time_range      - One of the following time ranges:\n"
            "                            YYYY/MM/DD[THH:MM:SS][-YYYY/MM/DD[THH:MM:SS]\n"
            "                            last <n> <hours|days|weeks|months>\n"
            "       The config file path is \"" + UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf\".");
}

bool ParseTimestamp(const std::string &timestamp, struct tm * const tm) {
    return TimeUtil::StringToStructTm(tm, timestamp, "%Y/%m/%d") or
           TimeUtil::StringToStructTm(tm, timestamp, "%Y/%m/%dT%H:%M:%S");
}

void ParseTimeRange(const std::string &range_string, time_t * const time_start, time_t * const time_end) {
    std::vector<std::string> tokens;

    if (StringUtil::StartsWith(range_string, "last ", true)) {
        StringUtil::SplitThenTrimWhite(range_string, ' ', &tokens, true);
        if (tokens.size() != 3)
            LOG_ERROR("invalid time range");

        unsigned time_atom(0);
        if (not StringUtil::ToUnsigned(tokens[1], &time_atom))
            LOG_ERROR("time atom has to be greater than zero");

        auto current_time(std::time(nullptr));
        unsigned long seconds_to_deduct(0);
        const auto granularity(tokens[2]);
        if (StringUtil::StartsWith(granularity, "hour", true))
            seconds_to_deduct = time_atom * 3600;
        else if (StringUtil::StartsWith(granularity, "day", true))
            seconds_to_deduct = time_atom * 24 * 3600;
        else if (StringUtil::StartsWith(granularity, "week", true))
            seconds_to_deduct = time_atom * 7 * 24 * 3600;
        else if (StringUtil::StartsWith(granularity, "month", true))
            seconds_to_deduct = time_atom * 31 * 24 * 3600;
        else
            LOG_ERROR("invalid time range");

        *time_start = current_time - seconds_to_deduct;
        *time_end = current_time;

        return;
    }

    struct tm start_time_buffer, end_time_buffer;
    std::memset(&start_time_buffer, 0, sizeof(struct tm));
    std::memset(&end_time_buffer, 0, sizeof(struct tm));

    if (ParseTimestamp(range_string, &start_time_buffer)) {
        *time_start = std::mktime(&start_time_buffer);
        *time_end = TimeUtil::BAD_TIME_T;
        if (*time_start == TimeUtil::BAD_TIME_T)
            LOG_ERROR("invalid time range");

        return;
    }

    if (StringUtil::Split(range_string, '-', &tokens, true) != 2)
        LOG_ERROR("invalid time range");

    if (not ParseTimestamp(tokens[0], &start_time_buffer) or
        not ParseTimestamp(tokens[1], &end_time_buffer))
    {
        LOG_ERROR("invalid time range");
    }

    *time_start = std::mktime(&start_time_buffer);
    *time_end = std::mktime(&end_time_buffer);
    if (*time_start == TimeUtil::BAD_TIME_T)
        LOG_ERROR("invalid time range");
}

struct Datapoint {
    std::string label_;
    time_t timestamp_;
    std::string value_;

    Datapoint(const std::string &label, const time_t timestamp, const std::string &value)
        : label_(label), timestamp_(timestamp), value_(value) {}

    bool operator<(const Datapoint &rhs) const {
        return this->timestamp_ < rhs.timestamp_;
    }
};

void LoadSystemMonitorLog(const std::string &log_path, const std::unordered_map<uint8_t, std::string> &ordinal_to_label_map,
                          std::vector<Datapoint> * const data)
{
    static constexpr size_t DATA_INITIAL_SIZE = 1000 * 1000;

    File log_file(log_path, "rb");
    int entry_num(1);
    std::vector<std::string> parts;
    data->reserve(DATA_INITIAL_SIZE);

    while (not log_file.eof()) {
        uint32_t timestamp;
        uint8_t ordinal;
        uint32_t value;

        BinaryIO::ReadOrDie(log_file, &timestamp);
        BinaryIO::ReadOrDie(log_file, &ordinal);
        BinaryIO::ReadOrDie(log_file, &value);

        if (ordinal_to_label_map.find(ordinal) == ordinal_to_label_map.end())
            LOG_ERROR("unknown ordinal " + std::to_string(ordinal) + " in log entry " + std::to_string(entry_num));

        data->emplace_back(ordinal_to_label_map.at(ordinal), static_cast<time_t>(timestamp), std::to_string(value));
        ++entry_num;
    }

    // sort by timestamp
    std::sort(data->begin(), data->end());
}

void GetDataRange(const time_t time_start, const time_t time_end, const std::vector<Datapoint> &data,
                  std::vector<Datapoint>::const_iterator * const begin, std::vector<Datapoint>::const_iterator * const end)
{
    // The list should be sorted at this point
    *begin = std::lower_bound(data.begin(), data.end(), Datapoint("", time_start, ""));
    *end = std::upper_bound(data.begin(), data.end(), Datapoint("", time_end, ""));
}

unsigned WritePlotDataToDisk(const std::string &output_path, const std::vector<std::string> &labels,
                         const std::vector<Datapoint>::const_iterator &data_begin, const std::vector<Datapoint>::const_iterator &data_end)
{
    // We expect the values of the labels to use the same axis/scale
    // Columns: Timestamp [Label 1..n]
    File plot_data(output_path, "w");

    plot_data.writeln("#\t" + StringUtil::Join(labels, '\t'));

    time_t current_write_timestamp(TimeUtil::BAD_TIME_T);
    std::map<std::string, std::string> current_write_timestamp_values;
    unsigned lines_written(0);

    for (auto itr(data_begin); itr != data_end; ++itr) {
        const auto datapoint(*itr);

        if (current_write_timestamp == datapoint.timestamp_) {
            current_write_timestamp_values[datapoint.label_] = datapoint.value_;
            continue;
        }

        if (not current_write_timestamp_values.empty()) {
            std::string out_line(std::to_string(current_write_timestamp) + "\t");
            for (const auto &label : labels) {
                const auto value(current_write_timestamp_values.find(label));
                if (value != current_write_timestamp_values.end())
                    out_line += value->second + "\t";
                else
                    out_line += "\t";
            }

            plot_data.writeln(out_line);
            ++lines_written;
        }

        current_write_timestamp = datapoint.timestamp_;
        current_write_timestamp_values[datapoint.label_] = datapoint.value_;
    }

    if (not current_write_timestamp_values.empty()) {
        std::string out_line(std::to_string(current_write_timestamp) + "\t");
        for (const auto &label : labels) {
            const auto value(current_write_timestamp_values.find(label));
            if (value != current_write_timestamp_values.end())
                out_line += value->second + "\t";
            else
                out_line += "\t";
        }

        plot_data.writeln(out_line);
        ++lines_written;
    }

    return lines_written;
}

void DisplayPlot(const std::string &data_path, const std::string &script_path, const std::string &plot_path) {
    if (not FileUtil::Exists(data_path))
        LOG_ERROR("data file for plotting does not exist at " + data_path);
    else if (not FileUtil::Exists(script_path))
        LOG_ERROR("script file for plotting does not exist at " + script_path);

    std::vector<std::string> gnuplot_args {
        "-c",
        script_path,
        data_path,
        plot_path,
    };
    ExecUtil::ExecOrDie("/usr/bin/gnuplot", gnuplot_args);

    std::vector<std::string> xdg_args {
        plot_path
    };

    ExecUtil::ExecOrDie("/usr/bin/xdg-open", xdg_args);
}

const std::set<std::string> VALID_SYSTEM_IDS{
    "nu", "ptah", "sobek", "ub15", "ub16", "ub28"
};

const std::set<std::string> VALID_COARSE_METRICS{
    "cpu", "mem", "disk"
};

void GetLabelsForCoarseMetric(const std::string &coarse_metric, std::vector<std::string> * const labels) {
    // this order needs to be observed in the plotting scripts
    if (coarse_metric == "mem") {
        labels->emplace_back("MemAvailable");
        labels->emplace_back("Unevictable");
        labels->emplace_back("SwapFree");
    } else if (coarse_metric == "cpu")
        labels->emplace_back("CPU");
    else if (coarse_metric == "disk")
        ;
    else
        LOG_ERROR("invalid coarse metric '" + coarse_metric + "'");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc < 4)
        Usage();

    const std::string OUTPUT_FILENAME_FLAG_PREFIX("--output-filename=");
    std::string output_filename;
    if (StringUtil::StartsWith(argv[1], OUTPUT_FILENAME_FLAG_PREFIX)) {
        output_filename = argv[1] + OUTPUT_FILENAME_FLAG_PREFIX.length();
        --argc, ++argv;
    }

    if (argc != 4)
        Usage();

    const std::string system_id(TextUtil::UTF8ToLower(argv[1]));
    const std::string coarse_metric(TextUtil::UTF8ToLower(argv[2]));
    const std::string time_range(argv[3]);

    if (VALID_SYSTEM_IDS.find(system_id) == VALID_SYSTEM_IDS.end())
        LOG_ERROR("invalid system ID '" + system_id + "'");
    else {
        const auto hostname(MiscUtil::SafeGetEnv("HOSTNAME"));
        if (not StringUtil::StartsWith(hostname, system_id, true))
            LOG_WARNING("attempting to view system monitor data of a system that is not the host. time range may be inaccurate");
    }

    time_t time_start, time_end;
    std::vector<Datapoint> log_data;
    std::vector<Datapoint>::const_iterator data_range_start, data_range_end;
    std::vector<std::string> labels;

    ParseTimeRange(time_range, &time_start, &time_end);
    GetLabelsForCoarseMetric(coarse_metric, &labels);

    const IniFile ini_file(UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf");
    const auto log_file(ini_file.getString("Logs", system_id));
    const auto plot_data_file(ini_file.getString("Default Plotting Inputs", coarse_metric));
    const auto plot_script_file(ini_file.getString("Plotting Scripts", coarse_metric));
    if (output_filename.empty())
        output_filename = ini_file.getString("Default Plotting Outputs", coarse_metric);

    std::unordered_map<uint8_t, std::string> ordinal_to_label_map;
    const IniFile monitor_ini_file(UBTools::GetTuelibPath() + "/system_monitor.conf");
    for (const auto &entry : *monitor_ini_file.getSection("Label Ordinals"))
        ordinal_to_label_map[StringUtil::ToUnsigned(entry.name_)] = entry.value_;

    LoadSystemMonitorLog(log_file, ordinal_to_label_map, &log_data);
    GetDataRange(time_start, time_end, log_data, &data_range_start, &data_range_end);

    if (data_range_start == log_data.end())
        LOG_ERROR("found no data that was newer than the given range's beginning");

    if (time_end == TimeUtil::BAD_TIME_T) {
        // print out the closest data point
        if (data_range_start->timestamp_ == time_start)
            LOG_INFO("Data for exact time point (" + TimeUtil::TimeTToString(time_start) + "):");
        else
            LOG_INFO("Data for closest time point (" + TimeUtil::TimeTToString(time_start) + "):");

        const auto datapoint_timestamp(data_range_start->timestamp_);
        while (data_range_start != log_data.end() and data_range_start->timestamp_ == datapoint_timestamp)
            LOG_INFO("\t" + data_range_start->label_ + " = " + data_range_start->value_);

        return EXIT_SUCCESS;
    }

    if (WritePlotDataToDisk(plot_data_file, labels, data_range_start, data_range_end) == 0)
        LOG_WARNING("found no data for the given time range");
    else
        DisplayPlot(plot_data_file, plot_script_file, output_filename);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>//
//  Copyright (C) 2015  Marcus Comstedt <marcus@mc.pp.se>
//
//  Permission to use, copy, modify, and/or distribute this software for any
//  purpose with or without fee is hereby granted, provided that the above
//  copyright notice and this permission notice appear in all copies.
//
//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
//  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
//  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
//  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
//  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//

#include <fstream>
#include <iostream>
#include <cstdint>
#include <memory>

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define log(...) fprintf(stderr, __VA_ARGS__);
#define info(...) do { if (log_level > 0) fprintf(stderr, __VA_ARGS__); } while (0)
#define error(...) do { fprintf(stderr, "%s: ", program_short_name); fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)

static char *program_short_name;

int log_level = 0;

static const int NUM_IMAGES = 4;
static const int HEADER_SIZE = 32;

static void align_offset(uint32_t &offset, int bits)
{
    uint32_t mask = (1 << bits) - 1;
    if (offset & mask)
        offset = (offset | mask) + 1;
}

static void write_byte(std::ostream &ofs, uint32_t &file_offset, uint8_t byte)
{
    ofs << byte;
    file_offset++;
}

static void write_bytes(std::ostream &ofs, uint32_t &file_offset,
                        const uint8_t *buf, size_t n)
{
    if (n > 0) {
        ofs.write(reinterpret_cast<const char*>(buf), n);
        file_offset += n;
    }
}

static void write_file(std::ostream &ofs, uint32_t &file_offset,
                       std::istream &ifs, const char *filename)
{
    const size_t bufsize = 8192;
    uint8_t *buffer = new uint8_t[bufsize];

    while(!ifs.eof()) {
        ifs.read(reinterpret_cast<char *>(buffer), bufsize);
        if (ifs.bad())
            error("can't read input image `%s': %s\n", filename, strerror(errno));
        write_bytes(ofs, file_offset, buffer, ifs.gcount());
    }

    delete[] buffer;
}

static void pad_to(std::ostream &ofs, uint32_t &file_offset, uint32_t target)
{
    if (target < file_offset)
        error("Trying to pad backwards!\n");
    while(file_offset < target)
        write_byte(ofs, file_offset, 0xff);
}

class Image {
    const char *filename;
    std::ifstream ifs;
    uint32_t offs;

public:
    Image(const char *filename);
    size_t size();
    void write(std::ostream &ofs, uint32_t &file_offset);
    void place(uint32_t o) { offs = o; }
    uint32_t offset() const { return offs; }
};

Image::Image(const char *filename) : filename(filename), ifs(filename, std::ifstream::binary)
{
    if (ifs.fail())
        error("can't open input image `%s': %s\n", filename, strerror(errno));
}

size_t Image::size()
{
    ifs.seekg (0, ifs.end);
    if (ifs.fail())
        error("can't seek on input image `%s': %s\n", filename, strerror(errno));
    size_t length = ifs.tellg();
    ifs.seekg (0, ifs.beg);
    if (ifs.fail())
        error("can't seek on input image `%s': %s\n", filename, strerror(errno));

    if (length == 0)
        error("input image `%s' doesn't contain any data\n", filename);
    return length;
}

void Image::write(std::ostream &ofs, uint32_t &file_offset)
{
    write_file(ofs, file_offset, ifs, filename);
}

static void write_header(std::ostream &ofs, uint32_t &file_offset,
                         Image const *image, bool coldboot)
{
    // Preamble
    write_byte(ofs, file_offset, 0x7e);
    write_byte(ofs, file_offset, 0xaa);
    write_byte(ofs, file_offset, 0x99);
    write_byte(ofs, file_offset, 0x7e);

    // Boot mode
    write_byte(ofs, file_offset, 0x92);
    write_byte(ofs, file_offset, 0x00);
    write_byte(ofs, file_offset, coldboot ? 0x10 : 0x00);

    // Boot address
    write_byte(ofs, file_offset, 0x44);
    write_byte(ofs, file_offset, 0x03);
    write_byte(ofs, file_offset, (image->offset() >> 16) & 0xff);
    write_byte(ofs, file_offset, (image->offset() >> 8) & 0xff);
    write_byte(ofs, file_offset, image->offset() & 0xff);

    // Bank offset
    write_byte(ofs, file_offset, 0x82);
    write_byte(ofs, file_offset, 0x00);
    write_byte(ofs, file_offset, 0x00);

    // Reboot
    write_byte(ofs, file_offset, 0x01);
    write_byte(ofs, file_offset, 0x08);

    // Zero out any unused bytes
    while (file_offset & (HEADER_SIZE - 1))
        write_byte(ofs, file_offset, 0x00);
}

void usage()
{
    log("\n");
    log("Usage: icemulti [options] input-files\n");
    log("\n");
    log(" -c\n");
    log(" coldboot mode, power on reset image is selected by CBSEL0/CBSEL1\n");
    log("\n");
    log(" -p0, -p1, -p2, -p3\n");
    log(" select power on reset image when not using coldboot mode\n");
    log("\n");
    log(" -a<n>, -A<n>\n");
    log(" align images at 2^<n> bytes. -A also aligns image 0.\n");
    log("\n");
    log(" -o filename\n");
    log(" write output image to file instead of stdout\n");
    log("\n");
    log(" -v\n");
    log(" verbose (repeat to increase verbosity)\n");
    log("\n");
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    int c;
    char *endptr = NULL;
    bool coldboot = false;
    int por_image = 0;
    int header_count = 0;
    int image_count = 0;
    int align_bits = 0;
    bool align_first = false;
    Image *header_images[NUM_IMAGES];
    std::unique_ptr<Image> images[NUM_IMAGES];
    const char *outfile_name = NULL;

    static struct option long_options[] = {
        {NULL, 0, NULL, 0}
    };

    program_short_name = strrchr(argv[0], '/');
    if (program_short_name == NULL)
        program_short_name = argv[0];
    else
        program_short_name++;

    while ((c = getopt_long(argc, argv, "cp:a:A:o:v",
                long_options, NULL)) != -1)
        switch (c) {
            case 'c':
                coldboot = true;
                break;
            case 'p':
                if (optarg[0] == '0' && optarg[1] == '\0')
                    por_image = 0;
                else if (optarg[0] == '1' && optarg[1] == '\0')
                    por_image = 1;
                else if (optarg[0] == '2' && optarg[1] == '\0')
                    por_image = 2;
                else if (optarg[0] == '3' && optarg[1] == '\0')
                    por_image = 3;
                else
                    error("`%s' is not a valid power-on/reset image (must be 0, 1, 2, or 3)\n", optarg);
                break;
            case 'A':
                align_first = true;
                /* fallthrough */
            case 'a':
                align_bits = strtol(optarg, &endptr, 0);
                if (*endptr != '\0')
                    error("`%s' is not a valid number\n", optarg);
                if (align_bits < 0)
                    error("argument to `-%c' must be non-negative\n", c);
                break;
            case 'o':
                outfile_name = optarg;
                break;
            case 'v':
                log_level++;
                break;
            default:
                usage();
        }

    if (optind == argc) {
        fprintf(stderr, "%s: missing argument\n", program_short_name);
        usage();
    }

    while (optind != argc) {
        if (header_count >= NUM_IMAGES)
            error("Too many images supplied\n");
        images[image_count].reset(new Image(argv[optind++]));
        header_images[header_count] = &*images[image_count];
        header_count++;
        image_count++;
    }

    if (coldboot && por_image != 0)
        error("Can't select power on reset boot image in cold boot mode\n");

    if (por_image >= header_count)
        error("Specified non-existing image for power on reset\n");

    // Place images
    uint32_t offs = (NUM_IMAGES + 1) * HEADER_SIZE;
    if (align_first)
        align_offset(offs, align_bits);
    for (int i=0; i<image_count; i++) {
        images[i]->place(offs);
        offs += images[i]->size();
        align_offset(offs, align_bits);
        info("Place image %d at %06x .. %06x.\n", i, int(images[i]->offset()), int(offs));
    }

    // Populate headers
    for (int i=header_count; i < NUM_IMAGES; i++)
        header_images[i] = header_images[por_image];

    std::ofstream ofs;
    std::ostream *osp;

    if (outfile_name != NULL) {
        ofs.open(outfile_name, std::ofstream::binary);
        if (!ofs.is_open())
            error("can't open output file `%s': %s\n", outfile_name, strerror(errno));
        osp = &ofs;
    } else {
        osp = &std::cout;
    }

    uint32_t file_offset = 0;
    for (int i=0; i<NUM_IMAGES + 1; i++)
    {
        pad_to(*osp, file_offset, i * HEADER_SIZE);
        if (i == 0)
            write_header(*osp, file_offset, header_images[por_image], coldboot);
        else
            write_header(*osp, file_offset, header_images[i - 1], false);
    }
    for (int i=0; i<image_count; i++)
    {
        pad_to(*osp, file_offset, images[i]->offset());
        images[i]->write(*osp, file_offset);
    }

    info("Done.\n");
    return EXIT_SUCCESS;
}
<commit_msg>icemulti: Treat offset printing like ordinary flag<commit_after>//
//  Copyright (C) 2015  Marcus Comstedt <marcus@mc.pp.se>
//
//  Permission to use, copy, modify, and/or distribute this software for any
//  purpose with or without fee is hereby granted, provided that the above
//  copyright notice and this permission notice appear in all copies.
//
//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
//  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
//  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
//  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
//  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//

#include <fstream>
#include <iostream>
#include <cstdint>
#include <memory>

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define log(...) fprintf(stderr, __VA_ARGS__);
#define error(...) do { fprintf(stderr, "%s: ", program_short_name); fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)

static char *program_short_name;

static const int NUM_IMAGES = 4;
static const int HEADER_SIZE = 32;

static void align_offset(uint32_t &offset, int bits)
{
    uint32_t mask = (1 << bits) - 1;
    if (offset & mask)
        offset = (offset | mask) + 1;
}

static void write_byte(std::ostream &ofs, uint32_t &file_offset, uint8_t byte)
{
    ofs << byte;
    file_offset++;
}

static void write_bytes(std::ostream &ofs, uint32_t &file_offset,
                        const uint8_t *buf, size_t n)
{
    if (n > 0) {
        ofs.write(reinterpret_cast<const char*>(buf), n);
        file_offset += n;
    }
}

static void write_file(std::ostream &ofs, uint32_t &file_offset,
                       std::istream &ifs, const char *filename)
{
    const size_t bufsize = 8192;
    uint8_t *buffer = new uint8_t[bufsize];

    while(!ifs.eof()) {
        ifs.read(reinterpret_cast<char *>(buffer), bufsize);
        if (ifs.bad())
            error("can't read input image `%s': %s\n", filename, strerror(errno));
        write_bytes(ofs, file_offset, buffer, ifs.gcount());
    }

    delete[] buffer;
}

static void pad_to(std::ostream &ofs, uint32_t &file_offset, uint32_t target)
{
    if (target < file_offset)
        error("Trying to pad backwards!\n");
    while(file_offset < target)
        write_byte(ofs, file_offset, 0xff);
}

class Image {
    const char *filename;
    std::ifstream ifs;
    uint32_t offs;

public:
    Image(const char *filename);
    size_t size();
    void write(std::ostream &ofs, uint32_t &file_offset);
    void place(uint32_t o) { offs = o; }
    uint32_t offset() const { return offs; }
};

Image::Image(const char *filename) : filename(filename), ifs(filename, std::ifstream::binary)
{
    if (ifs.fail())
        error("can't open input image `%s': %s\n", filename, strerror(errno));
}

size_t Image::size()
{
    ifs.seekg (0, ifs.end);
    if (ifs.fail())
        error("can't seek on input image `%s': %s\n", filename, strerror(errno));
    size_t length = ifs.tellg();
    ifs.seekg (0, ifs.beg);
    if (ifs.fail())
        error("can't seek on input image `%s': %s\n", filename, strerror(errno));

    if (length == 0)
        error("input image `%s' doesn't contain any data\n", filename);
    return length;
}

void Image::write(std::ostream &ofs, uint32_t &file_offset)
{
    write_file(ofs, file_offset, ifs, filename);
}

static void write_header(std::ostream &ofs, uint32_t &file_offset,
                         Image const *image, bool coldboot)
{
    // Preamble
    write_byte(ofs, file_offset, 0x7e);
    write_byte(ofs, file_offset, 0xaa);
    write_byte(ofs, file_offset, 0x99);
    write_byte(ofs, file_offset, 0x7e);

    // Boot mode
    write_byte(ofs, file_offset, 0x92);
    write_byte(ofs, file_offset, 0x00);
    write_byte(ofs, file_offset, coldboot ? 0x10 : 0x00);

    // Boot address
    write_byte(ofs, file_offset, 0x44);
    write_byte(ofs, file_offset, 0x03);
    write_byte(ofs, file_offset, (image->offset() >> 16) & 0xff);
    write_byte(ofs, file_offset, (image->offset() >> 8) & 0xff);
    write_byte(ofs, file_offset, image->offset() & 0xff);

    // Bank offset
    write_byte(ofs, file_offset, 0x82);
    write_byte(ofs, file_offset, 0x00);
    write_byte(ofs, file_offset, 0x00);

    // Reboot
    write_byte(ofs, file_offset, 0x01);
    write_byte(ofs, file_offset, 0x08);

    // Zero out any unused bytes
    while (file_offset & (HEADER_SIZE - 1))
        write_byte(ofs, file_offset, 0x00);
}

void usage()
{
    log("\n");
    log("Usage: icemulti [options] input-files\n");
    log("\n");
    log(" -c\n");
    log(" coldboot mode, power on reset image is selected by CBSEL0/CBSEL1\n");
    log("\n");
    log(" -p0, -p1, -p2, -p3\n");
    log(" select power on reset image when not using coldboot mode\n");
    log("\n");
    log(" -a<n>, -A<n>\n");
    log(" align images at 2^<n> bytes. -A also aligns image 0.\n");
    log("\n");
    log(" -o filename\n");
    log(" write output image to file instead of stdout\n");
    log("\n");
    log(" -v\n");
    log(" verbose (repeat to increase verbosity)\n");
    log("\n");
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    int c;
    char *endptr = NULL;
    bool coldboot = false;
    int por_image = 0;
    int header_count = 0;
    int image_count = 0;
    int align_bits = 0;
    bool align_first = false;
    Image *header_images[NUM_IMAGES];
    std::unique_ptr<Image> images[NUM_IMAGES];
    const char *outfile_name = NULL;
    bool print_offsets = false;

    static struct option long_options[] = {
        {NULL, 0, NULL, 0}
    };

    program_short_name = strrchr(argv[0], '/');
    if (program_short_name == NULL)
        program_short_name = argv[0];
    else
        program_short_name++;

    while ((c = getopt_long(argc, argv, "cp:a:A:o:v",
                long_options, NULL)) != -1)
        switch (c) {
            case 'c':
                coldboot = true;
                break;
            case 'p':
                if (optarg[0] == '0' && optarg[1] == '\0')
                    por_image = 0;
                else if (optarg[0] == '1' && optarg[1] == '\0')
                    por_image = 1;
                else if (optarg[0] == '2' && optarg[1] == '\0')
                    por_image = 2;
                else if (optarg[0] == '3' && optarg[1] == '\0')
                    por_image = 3;
                else
                    error("`%s' is not a valid power-on/reset image (must be 0, 1, 2, or 3)\n", optarg);
                break;
            case 'A':
                align_first = true;
                /* fallthrough */
            case 'a':
                align_bits = strtol(optarg, &endptr, 0);
                if (*endptr != '\0')
                    error("`%s' is not a valid number\n", optarg);
                if (align_bits < 0)
                    error("argument to `-%c' must be non-negative\n", c);
                break;
            case 'o':
                outfile_name = optarg;
                break;
            case 'v':
                print_offsets = true;
                break;
            default:
                usage();
        }

    if (optind == argc) {
        fprintf(stderr, "%s: missing argument\n", program_short_name);
        usage();
    }

    while (optind != argc) {
        if (header_count >= NUM_IMAGES)
            error("Too many images supplied\n");
        images[image_count].reset(new Image(argv[optind++]));
        header_images[header_count] = &*images[image_count];
        header_count++;
        image_count++;
    }

    if (coldboot && por_image != 0)
        error("Can't select power on reset boot image in cold boot mode\n");

    if (por_image >= header_count)
        error("Specified non-existing image for power on reset\n");

    // Place images
    uint32_t offs = (NUM_IMAGES + 1) * HEADER_SIZE;
    if (align_first)
        align_offset(offs, align_bits);
    for (int i=0; i<image_count; i++) {
        images[i]->place(offs);
        offs += images[i]->size();
        align_offset(offs, align_bits);
        if (print_offsets)
            fprintf(stderr, "Place image %d at %06x .. %06x.\n", i, int(images[i]->offset()), int(offs));
    }

    // Populate headers
    for (int i=header_count; i < NUM_IMAGES; i++)
        header_images[i] = header_images[por_image];

    std::ofstream ofs;
    std::ostream *osp;

    if (outfile_name != NULL) {
        ofs.open(outfile_name, std::ofstream::binary);
        if (!ofs.is_open())
            error("can't open output file `%s': %s\n", outfile_name, strerror(errno));
        osp = &ofs;
    } else {
        osp = &std::cout;
    }

    uint32_t file_offset = 0;
    for (int i=0; i<NUM_IMAGES + 1; i++)
    {
        pad_to(*osp, file_offset, i * HEADER_SIZE);
        if (i == 0)
            write_header(*osp, file_offset, header_images[por_image], coldboot);
        else
            write_header(*osp, file_offset, header_images[i - 1], false);
    }
    for (int i=0; i<image_count; i++)
    {
        pad_to(*osp, file_offset, images[i]->offset());
        images[i]->write(*osp, file_offset);
    }

    return EXIT_SUCCESS;
}
<|endoftext|>