CIRCT 24.0.0git
Loading...
Searching...
No Matches
Trace.cpp
Go to the documentation of this file.
1//===- Trace.cpp - Implementation of trace backend -----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT (lib/dialect/ESI/runtime/cpp/lib/).
12//
13//===----------------------------------------------------------------------===//
14
15#include "esi/backends/Trace.h"
16
17#include "esi/Accelerator.h"
18#include "esi/Services.h"
19#include "esi/Utils.h"
20
21#include <cassert>
22#include <cstdlib>
23#include <fstream>
24#include <iostream>
25#include <memory>
26#include <new>
27#include <sstream>
28
29using namespace esi;
30using namespace esi::services;
31using namespace esi::backends::trace;
32
33namespace {
34class TraceChannelPort;
35class TraceEngine;
36} // namespace
37
39
41 friend class TraceAccelerator;
42 Impl(Mode mode, std::filesystem::path manifestJson,
43 std::filesystem::path traceFile)
45 if (!std::filesystem::exists(manifestJson))
46 throw std::runtime_error("manifest file '" + manifestJson.string() +
47 "' does not exist");
48
49 if (mode == Write) {
50 // Open the trace file for writing.
51 traceWrite = new std::ofstream(traceFile);
52 if (!traceWrite->is_open())
53 throw std::runtime_error("failed to open trace file '" +
54 traceFile.string() + "'");
55 } else if (mode == Discard) {
56 traceWrite = nullptr;
57 } else {
58 assert(false && "not implemented");
59 }
60 }
61
63 if (traceWrite) {
64 traceWrite->close();
65 delete traceWrite;
66 }
67 }
68
70 AppIDPath idPath, const ServiceImplDetails &details,
71 const HWClientDetails &clients);
72
73 void adoptChannelPort(ChannelPort *port) { channels.emplace_back(port); }
74
75 void write(const AppIDPath &id, const std::string &portName, const void *data,
76 size_t size, const std::string &prefix = "");
77 std::ostream &write(std::string service) {
78 assert(traceWrite && "traceWrite is null");
79 *traceWrite << "[" << service << "] ";
80 return *traceWrite;
81 }
82 bool isWriteable() { return traceWrite; }
83
84private:
85 std::ofstream *traceWrite;
86 std::filesystem::path manifestJson;
87 std::filesystem::path traceFile;
88 std::vector<std::unique_ptr<ChannelPort>> channels;
89};
90
91void TraceAccelerator::Impl::write(const AppIDPath &id,
92 const std::string &portName,
93 const void *data, size_t size,
94 const std::string &prefix) {
95 if (!isWriteable())
96 return;
97 std::string b64data;
98 utils::encodeBase64(data, size, b64data);
99
100 *traceWrite << prefix << (prefix.empty() ? "w" : "W") << "rite " << id << '.'
101 << portName << ": " << b64data << std::endl;
102}
103
104std::unique_ptr<AcceleratorConnection>
105TraceAccelerator::connect(Context &ctxt, std::string connectionString) {
106 std::string manifestPath;
107 std::string traceFile = "trace.log";
108
109 // Parse the connection std::string.
110 // Format: <mode>SEP<manifest path>[SEP<traceFile>]
111 // where SEP is ':' on Unix and ';' on Windows.
112 // Using different separators avoids conflicts with Windows drive letters
113 // (e.g. "C:/...").
114#ifdef _WIN32
115 constexpr char SEP = ';';
116#else
117 constexpr char SEP = ':';
118#endif
119
120 if (connectionString.size() < 2 || connectionString[1] != SEP)
121 throw std::runtime_error(
122 std::string("connection string must be of the form ") + "'<mode>" +
123 SEP + "<manifest path>[" + SEP + "<traceFile>]'");
124
125 char modeChar = connectionString[0];
126 std::string rest = connectionString.substr(2);
127
128 // Find the optional trace file, separated by the last SEP.
129 size_t lastSep = rest.rfind(SEP);
130 if (lastSep != std::string::npos) {
131 manifestPath = rest.substr(0, lastSep);
132 traceFile = rest.substr(lastSep + 1);
133
134 // Validate that the trace file name is non-empty when present.
135 if (traceFile.empty())
136 throw std::runtime_error(
137 std::string("connection string must be of the form ") + "'<mode>" +
138 SEP + "<manifest path>[" + SEP +
139 "<traceFile>]': "
140 "trace file name cannot be empty");
141 } else {
142 manifestPath = rest;
143 }
144
145 // Validate that manifest path is non-empty.
146 if (manifestPath.empty())
147 throw std::runtime_error(
148 std::string("connection string must be of the form ") + "'<mode>" +
149 SEP + "<manifest path>[" + SEP +
150 "<traceFile>]': "
151 "manifest path cannot be empty");
152
153 // Parse the mode.
154 Mode mode;
155 if (modeChar == 'w')
156 mode = Write;
157 else if (modeChar == '-')
158 mode = Discard;
159 else
160 throw std::runtime_error("unknown mode '" + std::string(1, modeChar) + "'");
161
162 return std::make_unique<TraceAccelerator>(ctxt, mode,
163 std::filesystem::path(manifestPath),
164 std::filesystem::path(traceFile));
165}
166
168 std::filesystem::path manifestJson,
169 std::filesystem::path traceFile)
171 impl = std::make_unique<Impl>(mode, manifestJson, traceFile);
172}
174
175namespace {
176class TraceSysInfo : public SysInfo {
177public:
178 TraceSysInfo(AcceleratorConnection &conn, std::filesystem::path manifestJson)
179 : SysInfo(conn), manifestJson(manifestJson) {}
180
181 uint32_t getEsiVersion() const override {
182 return Manifest(conn.getCtxt(), getJsonManifest()).getApiVersion();
183 }
184
185 std::string getJsonManifest() const override {
186 // Read in the whole json file and return it.
187 std::ifstream manifest(manifestJson);
188 if (!manifest.is_open())
189 throw std::runtime_error("failed to open manifest file '" +
190 manifestJson.string() + "'");
191 std::stringstream buffer;
192 buffer << manifest.rdbuf();
193 manifest.close();
194 return buffer.str();
195 }
196
197 std::vector<uint8_t> getCompressedManifest() const override {
198 throw std::runtime_error(
199 "compressed manifest not supported by trace backend");
200 }
201
202private:
203 std::filesystem::path manifestJson;
204};
205} // namespace
206
207namespace {
208class WriteTraceChannelPort : public WriteChannelPort {
209public:
210 WriteTraceChannelPort(TraceAccelerator::Impl &impl, const Type *type,
211 const AppIDPath &id, const std::string &portName)
212 : WriteChannelPort(type), impl(impl), id(id), portName(portName) {}
213
214protected:
215 void writeImpl(const MessageData &data) override {
216 impl.write(id, portName, data.getBytes(), data.getSize());
217 }
218
219 bool tryWriteImpl(const MessageData &data) override {
220 impl.write(id, portName, data.getBytes(), data.getSize(), "try");
221 return true;
222 }
223
224 TraceAccelerator::Impl &impl;
225 AppIDPath id;
226 std::string portName;
227};
228} // namespace
229
230namespace {
231class ReadTraceChannelPort : public ReadChannelPort {
232public:
233 ReadTraceChannelPort(TraceAccelerator::Impl &impl, const Type *type)
234 : ReadChannelPort(type) {}
235 ~ReadTraceChannelPort() { disconnect(); }
236
237private:
238 MessageData genMessage() {
239 std::ptrdiff_t numBits = getType()->getBitWidth();
240 if (numBits < 0)
241 // TODO: support other types.
242 throw std::runtime_error("unsupported type for read: " +
243 getType()->getID());
244
245 std::ptrdiff_t size = (numBits + 7) / 8;
246 // Zero-width types need a 1-byte placeholder.
247 if (size == 0)
248 return MessageData({0});
249 std::vector<uint8_t> bytes(size);
250 for (std::ptrdiff_t i = 0; i < size; ++i)
251 bytes[i] = rand() % 256;
252 return MessageData(bytes);
253 }
254
255 bool pollImpl() override {
256 if (!pendingMessage)
257 pendingMessage = std::make_unique<MessageData>(genMessage());
258 if (!invokeCallback(pendingMessage))
259 return false;
260 pendingMessage.reset();
261 return true;
262 }
263
264 std::unique_ptr<SegmentedMessageData> pendingMessage;
265};
266} // namespace
267
268namespace {
269class TraceEngine : public Engine {
270public:
271 TraceEngine(AcceleratorConnection &conn, TraceAccelerator::Impl &impl)
272 : Engine(conn), impl(impl) {}
273
274 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
275 const std::string &channelName,
276 BundleType::Direction dir,
277 const Type *type) override {
278 std::unique_ptr<ChannelPort> port;
279 if (BundlePort::isWrite(dir))
280 port = std::make_unique<WriteTraceChannelPort>(impl, type, idPath,
281 channelName);
282 else
283 port = std::make_unique<ReadTraceChannelPort>(impl, type);
284 return port;
285 }
286
287private:
288 TraceAccelerator::Impl &impl;
289};
290} // namespace
291
292void TraceAccelerator::createEngine(const std::string &dmaEngineName,
293 AppIDPath idPath,
294 const ServiceImplDetails &details,
295 const HWClientDetails &clients) {
296 registerEngine(idPath, std::make_unique<TraceEngine>(*this, getImpl()),
297 clients);
298}
299
300class TraceMMIO : public MMIO {
301public:
303 const HWClientDetails &clients)
304 : MMIO(conn, idPath, clients), impl(conn.getImpl()) {}
305
306 virtual uint64_t read(uint32_t addr) const override {
307 uint64_t data = rand();
308 if (impl.isWriteable())
309 impl.write("MMIO") << "[" << std::hex << addr << "] -> " << data
310 << std::endl;
311 return data;
312 }
313 virtual void write(uint32_t addr, uint64_t data) override {
314 if (!impl.isWriteable())
315 return;
316 impl.write("MMIO") << "[" << std::hex << addr << "] <- " << data
317 << std::endl;
318 }
319
320private:
322};
323
324class TraceHostMem : public HostMem {
325public:
327
332 if (ptr && impl.isWriteable())
333 impl.write("HostMem") << "free " << ptr << std::endl;
334 free(ptr);
335 }
336 virtual void *getPtr() const override { return ptr; }
337 virtual std::size_t getSize() const override { return size; }
338
339 private:
340 void *ptr;
341 std::size_t size;
343 };
344
345 virtual std::unique_ptr<HostMemRegion>
346 allocate(std::size_t size, HostMem::Options opts) const override {
347 if (size == 0)
348 return nullptr;
349 std::unique_ptr<HostMemRegion> ret(new (std::nothrow)
350 TraceHostMemRegion(size, impl));
351 if (!ret || !ret->getPtr())
352 return nullptr;
353 if (impl.isWriteable())
354 impl.write("HostMem 0x")
355 << ret->getPtr() << " allocate " << size
356 << " bytes. Writeable: " << opts.writeable
357 << ", useLargePages: " << opts.useLargePages << std::endl;
358 return ret;
359 }
360 virtual bool mapMemory(void *ptr, std::size_t size,
361 HostMem::Options opts) const override {
362
363 if (impl.isWriteable())
364 impl.write("HostMem")
365 << "map 0x" << ptr << " size " << size
366 << " bytes. Writeable: " << opts.writeable
367 << ", useLargePages: " << opts.useLargePages << std::endl;
368 return true;
369 }
370 virtual void unmapMemory(void *ptr) const override {
371 if (impl.isWriteable())
372 impl.write("HostMem") << "unmap 0x" << ptr << std::endl;
373 }
374
375private:
377};
378
380 AppIDPath idPath, std::string implName,
381 const ServiceImplDetails &details,
382 const HWClientDetails &clients) {
383 if (svcType == typeid(SysInfo))
384 return new TraceSysInfo(*this, getImpl().manifestJson);
385 if (svcType == typeid(MMIO))
386 return new TraceMMIO(*this, idPath, clients);
387 if (svcType == typeid(HostMem))
388 return new TraceHostMem(*this);
389 return nullptr;
390}
391
#define REGISTER_ACCELERATOR(Name, TAccelerator)
assert(baseType &&"element must be base type")
if(parseOptionalKeywordOrOptionalString(parser, result.rawName, found)) return failure()
virtual std::unique_ptr< HostMemRegion > allocate(std::size_t size, HostMem::Options opts) const override
Definition Trace.cpp:346
TraceHostMem(TraceAccelerator &conn)
Definition Trace.cpp:326
virtual bool mapMemory(void *ptr, std::size_t size, HostMem::Options opts) const override
Definition Trace.cpp:360
virtual void unmapMemory(void *ptr) const override
Unmap memory which was previously mapped with 'mapMemory'.
Definition Trace.cpp:370
TraceAccelerator::Impl & impl
Definition Trace.cpp:376
TraceMMIO(TraceAccelerator &conn, const AppIDPath &idPath, const HWClientDetails &clients)
Definition Trace.cpp:302
virtual uint64_t read(uint32_t addr) const override
Read a 64-bit value from the global MMIO space.
Definition Trace.cpp:306
virtual void write(uint32_t addr, uint64_t data) override
Write a 64-bit value to the global MMIO space.
Definition Trace.cpp:313
TraceAccelerator::Impl & impl
Definition Trace.cpp:321
Abstract class representing a connection to an accelerator.
services::Service Service
Context & ctxt
ESI accelerator context.
void registerEngine(AppIDPath idPath, std::unique_ptr< Engine > engine, const HWClientDetails &clients)
If createEngine is overridden, this method should be called to register the engine and all of the cha...
virtual void disconnect()
Disconnect from the accelerator cleanly.
AcceleratorConnection(Context &ctxt)
Unidirectional channels are the basic communication primitive between the host and accelerator.
Definition Ports.h:115
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition Context.h:34
Class to parse a manifest.
Definition Manifest.h:39
uint32_t getApiVersion() const
Connect to an ESI simulation.
Definition Trace.h:35
std::unique_ptr< Impl > impl
Definition Trace.h:84
TraceAccelerator(Context &, Mode mode, std::filesystem::path manifestJson, std::filesystem::path traceFile)
Create a trace-based accelerator backend.
Definition Trace.cpp:167
void createEngine(const std::string &engineTypeName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients) override
Create a new engine for channel communication with the accelerator.
Definition Trace.cpp:292
virtual Service * createService(Service::Type service, AppIDPath idPath, std::string implName, const ServiceImplDetails &details, const HWClientDetails &clients) override
Called by getServiceImpl exclusively.
Definition Trace.cpp:379
static std::unique_ptr< AcceleratorConnection > connect(Context &, std::string connectionString)
Parse the connection string and instantiate the accelerator.
Definition Trace.cpp:105
services::HostMemRegion HostMemRegion
RAII memory region for host memory. See services::HostMemRegion.
Definition Services.h:289
MMIO(AcceleratorConnection &, const AppIDPath &idPath, const HWClientDetails &clients)
Definition Services.cpp:56
const std::type_info & Type
Definition Services.h:61
AcceleratorConnection & conn
Definition Services.h:88
Information about the Accelerator system.
Definition Services.h:113
void encodeBase64(const void *data, size_t size, std::string &out)
Definition Utils.cpp:23
Definition esi.py:1
std::vector< HWClientDetail > HWClientDetails
Definition Common.h:108
std::map< std::string, std::any > ServiceImplDetails
Definition Common.h:109
virtual std::size_t getSize() const override
Definition Trace.cpp:337
TraceHostMemRegion(std::size_t size, TraceAccelerator::Impl &impl)
Definition Trace.cpp:329
virtual void * getPtr() const override
Get a pointer to the host memory.
Definition Trace.cpp:336
TraceAccelerator::Impl & impl
Definition Trace.cpp:342
std::vector< std::unique_ptr< ChannelPort > > channels
Definition Trace.cpp:88
std::ostream & write(std::string service)
Definition Trace.cpp:77
void adoptChannelPort(ChannelPort *port)
Definition Trace.cpp:73
Service * createService(TraceAccelerator &conn, Service::Type svcType, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Impl(Mode mode, std::filesystem::path manifestJson, std::filesystem::path traceFile)
Definition Trace.cpp:42
Options for allocating host memory.
Definition Services.h:268