1 //
2 // Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include <client/src/ProfilingConnectionDumpToFileDecorator.hpp>
7
8 #include <Runtime.hpp>
9
10 #include <armnnUtils/Filesystem.hpp>
11
12 #include <common/include/IgnoreUnused.hpp>
13 #include <common/include/NumericCast.hpp>
14
15
16 #include <fstream>
17 #include <sstream>
18
19 #include <doctest/doctest.h>
20
21 using namespace arm::pipe;
22
23 namespace
24 {
25
26 const std::vector<char> g_Data = { 'd', 'u', 'm', 'm', 'y' };
27 const uint32_t g_DataLength = arm::pipe::numeric_cast<uint32_t>(g_Data.size());
28 const unsigned char* g_DataPtr = reinterpret_cast<const unsigned char*>(g_Data.data());
29
30 class DummyProfilingConnection : public IProfilingConnection
31 {
32 public:
DummyProfilingConnection()33 DummyProfilingConnection()
34 : m_Open(true)
35 , m_PacketData(std::make_unique<unsigned char[]>(g_DataLength))
36 {
37 // populate packet data and construct packet
38 std::memcpy(m_PacketData.get(), g_DataPtr, g_DataLength);
39 m_Packet = std::make_unique<Packet>(0u, g_DataLength, m_PacketData);
40 }
41
42 ~DummyProfilingConnection() = default;
43
IsOpen() const44 bool IsOpen() const override
45 {
46 return m_Open;
47 }
48
Close()49 void Close() override
50 {
51 m_Open = false;
52 }
53
WritePacket(const unsigned char * buffer,uint32_t length)54 bool WritePacket(const unsigned char* buffer, uint32_t length) override
55 {
56 arm::pipe::IgnoreUnused(buffer);
57 arm::pipe::IgnoreUnused(length);
58 return true;
59 }
60
ReadPacket(uint32_t timeout)61 Packet ReadPacket(uint32_t timeout) override
62 {
63 arm::pipe::IgnoreUnused(timeout);
64 return std::move(*m_Packet);
65 }
66
67 private:
68 bool m_Open;
69 std::unique_ptr<unsigned char[]> m_PacketData;
70 std::unique_ptr<Packet> m_Packet;
71 };
72
ReadDumpFile(const std::string & dumpFileName)73 std::vector<char> ReadDumpFile(const std::string& dumpFileName)
74 {
75 std::ifstream input(dumpFileName, std::ios::binary);
76 return std::vector<char>(std::istreambuf_iterator<char>(input), {});
77 }
78
79 } // anonymous namespace
80
81 TEST_SUITE("ProfilingConnectionDumpToFileDecoratorTests")
82 {
83 TEST_CASE("DumpIncomingInvalidFile")
84 {
85 ProfilingOptions options;
86 options.m_IncomingCaptureFile = "/";
87 options.m_OutgoingCaptureFile = "";
88 ProfilingConnectionDumpToFileDecorator decorator(std::make_unique<DummyProfilingConnection>(), options, false);
89 CHECK_THROWS_AS(decorator.ReadPacket(0), arm::pipe::ProfilingException);
90 }
91
92 TEST_CASE("DumpIncomingInvalidFileIgnoreErrors")
93 {
94 ProfilingOptions options;
95 options.m_IncomingCaptureFile = "/";
96 options.m_OutgoingCaptureFile = "";
97 ProfilingConnectionDumpToFileDecorator decorator(std::make_unique<DummyProfilingConnection>(), options, true);
98 CHECK_NOTHROW(decorator.ReadPacket(0));
99 }
100
101 TEST_CASE("DumpIncomingValidFile")
102 {
103 fs::path fileName = armnnUtils::Filesystem::NamedTempFile("Armnn-DumpIncomingValidFileTest-TempFile");
104
105 ProfilingOptions options;
106 options.m_IncomingCaptureFile = fileName.string();
107 options.m_OutgoingCaptureFile = "";
108
109 ProfilingConnectionDumpToFileDecorator decorator(std::make_unique<DummyProfilingConnection>(), options, false);
110
111 // NOTE: unique_ptr is needed here because operator=() is deleted for Packet
112 std::unique_ptr<Packet> packet;
113 CHECK_NOTHROW(packet = std::make_unique<Packet>(decorator.ReadPacket(0)));
114
115 decorator.Close();
116
117 std::vector<char> data = ReadDumpFile(options.m_IncomingCaptureFile);
118 const char* packetData = reinterpret_cast<const char*>(packet->GetData());
119
120 // check if the data read back from the dump file matches the original
121 constexpr unsigned int bytesToSkip = 2u * sizeof(uint32_t); // skip header and packet length
122 int diff = std::strncmp(data.data() + bytesToSkip, packetData, g_DataLength);
123 CHECK(diff == 0);
124 fs::remove(fileName);
125 }
126
127 TEST_CASE("DumpOutgoingInvalidFile")
128 {
129 ProfilingOptions options;
130 options.m_IncomingCaptureFile = "";
131 options.m_OutgoingCaptureFile = "/";
132 ProfilingConnectionDumpToFileDecorator decorator(std::make_unique<DummyProfilingConnection>(), options, false);
133 CHECK_THROWS_AS(decorator.WritePacket(g_DataPtr, g_DataLength), arm::pipe::ProfilingException);
134 }
135
136 TEST_CASE("DumpOutgoingInvalidFileIgnoreErrors")
137 {
138 ProfilingOptions options;
139 options.m_IncomingCaptureFile = "";
140 options.m_OutgoingCaptureFile = "/";
141
142 ProfilingConnectionDumpToFileDecorator decorator(std::make_unique<DummyProfilingConnection>(), options, true);
143 CHECK_NOTHROW(decorator.WritePacket(g_DataPtr, g_DataLength));
144
145 bool success = decorator.WritePacket(g_DataPtr, g_DataLength);
146 CHECK(!success);
147 }
148
149 TEST_CASE("DumpOutgoingValidFile")
150 {
151 fs::path fileName = armnnUtils::Filesystem::NamedTempFile("Armnn-DumpOutgoingValidFileTest-TempFile");
152
153 ProfilingOptions options;
154 options.m_IncomingCaptureFile = "";
155 options.m_OutgoingCaptureFile = fileName.string();
156
157 ProfilingConnectionDumpToFileDecorator decorator(std::make_unique<DummyProfilingConnection>(), options, false);
158
159 bool success = false;
160 CHECK_NOTHROW(success = decorator.WritePacket(g_DataPtr, g_DataLength));
161 CHECK(success);
162
163 decorator.Close();
164
165 std::vector<char> data = ReadDumpFile(options.m_OutgoingCaptureFile);
166
167 // check if the data read back from the dump file matches the original
168 int diff = std::strncmp(data.data(), g_Data.data(), g_DataLength);
169 CHECK(diff == 0);
170 fs::remove(fileName);
171 }
172
173 }
174