1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <soulng/util/Trace.hpp>
  7 #include <soulng/util/CodeFormatter.hpp>
  8 #include <soulng/util/Error.hpp>
  9 #include <soulng/util/Path.hpp>
 10 #include <soulng/util/BinaryReader.hpp>
 11 #include <soulng/util/BinaryWriter.hpp>
 12 #include <boost/filesystem.hpp>
 13 #include <boost/lexical_cast.hpp>
 14 #include <chrono>
 15 #include <thread>
 16 #include <vector>
 17 #include <stdexcept>
 18 #include <mutex>
 19 
 20 namespace soulng { namespace util {
 21 
 22 std::string CmajorRootDir()
 23 {
 24     char* e = getenv("CMAJOR_ROOT");
 25     if (e == nullptr || !*e)
 26     {
 27         throw std::runtime_error("please set 'CMAJOR_ROOT' environment variable to contain /path/to/cmajor directory.");
 28     }
 29     return std::string(e);
 30 }
 31 
 32 std::string TraceDir()
 33 {
 34     std::string root = CmajorRootDir();
 35     std::string traceDir = GetFullPath(Path::Combine(root"trace"));
 36     boost::filesystem::create_directories(traceDir);
 37     return traceDir;
 38 }
 39 
 40 std::string ConfigDir()
 41 {
 42     std::string root = CmajorRootDir();
 43     std::string configDir = GetFullPath(Path::Combine(root"config"));
 44     boost::filesystem::create_directories(configDir);
 45     return configDir;
 46 }
 47 
 48 std::string TraceFilePath()
 49 {
 50     std::string traceFilePath = GetFullPath(Path::Combine(TraceDir()"trace.bin"));
 51     return traceFilePath;
 52 }
 53 
 54 std::string WinMsgFilePath()
 55 {
 56     std::string winMsgFilePath = GetFullPath(Path::Combine(ConfigDir()"winmsg.txt"));
 57     return winMsgFilePath;
 58 }
 59 
 60 class TraceTable 
 61 {
 62 public:
 63     static void Init();
 64     static void Done();
 65     static TraceTable& Instance() { return *instance; }
 66     int32_t GetTraceFunctionId(const std::string& functionFullName);
 67     const std::string& GetTraceFunctionName(int32_t traceFunctionId) const;
 68     const std::string& GetTraceMessageName(int32_t traceMessageId) const;
 69     void Read();
 70     void Write();
 71 private:
 72     std::mutex mtx;
 73     static std::unique_ptr<TraceTable> instance;
 74     std::vector<std::string> traceFunctions;
 75     std::map<std::stringint32_t> traceFunctionMap;
 76     std::map<int32_tstd::string> traceMessageMap;
 77     std::string unknownMessage;
 78     TraceTable();
 79 };
 80 
 81 std::string TraceTablePath()
 82 {
 83     return GetFullPath(Path::Combine(TraceDir()"trace.tab"));
 84 }
 85 
 86 std::unique_ptr<TraceTable> TraceTable::instance;
 87 
 88 void TraceTable::Init()
 89 {
 90     instance.reset(new TraceTable());
 91 }
 92 
 93 void TraceTable::Done()
 94 {
 95     instance.reset();
 96 }
 97 
 98 TraceTable::TraceTable() : unknownMessage("<UNKNOWN_WINDOWS_MESSSAGE>")
 99 {
100 }
101 
102 int32_t TraceTable::GetTraceFunctionId(const std::string& functionFullName)
103 {
104     std::lock_guard<std::mutex> lock(mtx);
105     auto it = traceFunctionMap.find(functionFullName);
106     if (it != traceFunctionMap.cend())
107     {
108         return it->second;
109     }
110     int32_t traceFunctionId = traceFunctions.size();
111     traceFunctions.push_back(functionFullName);
112     traceFunctionMap[functionFullName] = traceFunctionId;
113     return traceFunctionId;
114 }
115 
116 const std::string& TraceTable::GetTraceFunctionName(int32_t traceFunctionId) const
117 {
118     Assert(traceFunctionId >= 0 && traceFunctionId < traceFunctions.size()"invalid trace function id");
119     return traceFunctions[traceFunctionId];
120 }
121 
122 const std::string& TraceTable::GetTraceMessageName(int32_t traceMessageId) const
123 {
124     auto it = traceMessageMap.find(traceMessageId);
125     if (it != traceMessageMap.cend())
126     {
127         return it->second;
128     }
129     else
130     {
131         return unknownMessage;
132     }
133 }
134 
135 void TraceTable::Read()
136 {
137     std::string traceTablePath = TraceTablePath();
138     std::ifstream traceTableFile(traceTablePath);
139     std::string line;
140     while (std::getline(traceTableFileline))
141     {
142         std::string::size_type colonPos = line.find(':');
143         std::string indexStr = line.substr(0colonPos);
144         int32_t index = boost::lexical_cast<int32_t>(indexStr);
145         std::string functionName = line.substr(colonPos + 1);
146         Assert(index == traceFunctions.size()"index invalid");
147         traceFunctions.push_back(functionName);
148         traceFunctionMap[functionName] = index;
149     }
150     std::string winMsgFilePath = WinMsgFilePath();
151     std::ifstream winMsgFile(winMsgFilePath);
152     std::string msgLine;
153     while (std::getline(winMsgFilemsgLine))
154     {
155         if (msgLine.empty()) continue;
156         std::string::size_type colonPos = msgLine.find(':');
157         if (colonPos == std::string::npos) continue;
158         std::string indexStr = msgLine.substr(0colonPos);
159         int32_t index = boost::lexical_cast<int32_t>(indexStr);
160         std::string messageName = msgLine.substr(colonPos + 1);
161         if (traceMessageMap.find(index) == traceMessageMap.cend())
162         {
163             traceMessageMap[index] = messageName;
164         }
165     }
166 }
167 
168 void TraceTable::Write()
169 {
170     std::string traceTablePath = TraceTablePath();
171     std::ofstream traceTableFile(traceTablePath);
172     CodeFormatter formatter(traceTableFile);
173     int32_t n = traceFunctions.size();
174     for (int32_t i = 0; i < n; ++i)
175     {
176         auto it = traceFunctionMap.find(traceFunctions[i]);
177         int32_t index = -1;
178         if (it != traceFunctionMap.cend())
179         {
180             index = it->second;
181         }
182         Assert(index == i"index invalid");
183         formatter << i << ":" << traceFunctions[i] << std::endl;
184     }
185 }
186 
187 void ReadTraceTable()
188 {
189     TraceTable::Instance().Read();
190 }
191 
192 void WriteTraceTable()
193 {
194     TraceTable::Instance().Write();
195 }
196 
197 int32_t GetTraceFunctionId(const std::string& functionFullName)
198 {
199     return TraceTable::Instance().GetTraceFunctionId(functionFullName);
200 }
201 
202 const std::string& GetTraceFunctionName(int32_t traceFunctionId)
203 {
204     return TraceTable::Instance().GetTraceFunctionName(traceFunctionId);
205 }
206 
207 const std::string& GetTraceMessageName(int32_t traceMessageId)
208 {
209     return TraceTable::Instance().GetTraceMessageName(traceMessageId);
210 }
211 
212 TraceEntry::TraceEntry(EntryKind kind_char threadId_int32_t id_int64_t nanosecs_) : kind(kind_)threadId(threadId_)id(id_)nanosecs(nanosecs_)
213 {
214 }
215 
216 class Trace 
217 {
218 public:
219     static void Init();
220     static void Done();
221     static Trace& Instance() { return *instance; }
222     void AddEntry(const TraceEntry& entry);
223     void Write();
224 private:
225     static std::unique_ptr<Trace> instance;
226     std::vector<TraceEntry> entries;
227     std::mutex mtx;
228 };
229 
230 std::unique_ptr<Trace> Trace::instance;
231 
232 void Trace::Init()
233 {
234     instance.reset(new Trace());
235 }
236 
237 void Trace::Done()
238 {
239     instance.reset();
240 }
241 
242 void Trace::AddEntry(const TraceEntry& entry)
243 {
244     std::lock_guard<std::mutex> lock(mtx);
245     entries.push_back(entry);
246 }
247 
248 void Trace::Write()
249 {
250     BinaryWriter writer(TraceFilePath());
251     int32_t n = entries.size();
252     writer.Write(n);
253     for (int32_t i = 0; i < n; ++i)
254     {
255         const TraceEntry& entry = entries[i];
256         writer.Write(static_cast<int8_t>(entry.kind));
257         writer.Write(entry.threadId);
258         writer.Write(entry.id);
259         writer.Write(entry.nanosecs);
260     }
261 }
262 
263 std::std::vector<TraceEntry>ReadTrace(conststd::string&traceFilePath)
264 {
265     std::vector<TraceEntry> trace;
266     BinaryReader reader(traceFilePath);
267     int32_t n = reader.ReadInt();
268     for (int32_t i = 0; i < n; ++i)
269     {
270         EntryKind kind = static_cast<EntryKind>(reader.ReadSByte());
271         char threadId = reader.ReadChar();
272         int32_t id = reader.ReadInt();
273         int64_t nanosecs = reader.ReadLong();
274         TraceEntry entry(kindthreadIdidnanosecs);
275         trace.push_back(std::move(entry));
276     }
277     return trace;
278 }
279 
280 bool tracing = false;
281 
282 std::chrono::time_point<std::chrono::steady_clock> start;
283 
284 void BeginTracing()
285 {
286     tracing = true;
287     start = std::chrono::steady_clock::now();
288 }
289 
290 void EndTracing()
291 {
292     tracing = false;
293     Trace::Instance().Write();
294 }
295 
296 #ifdef _WIN32
297 
298 #else
299          char threadId = '0';
300 #endif
301 
302 
303 void SetThreadId(char threadId_)
304 {
305     threadId = threadId_;
306 }
307 
308 void AddTraceEntry(EntryKind kindint32_t id)
309 {
310     if (!tracing) return;
311     std::chrono::steady_clock::duration time = std::chrono::steady_clock::now() - start;
312     TraceEntry entry(kindthreadIdidstd::chrono::duration_cast<std::chrono::nanoseconds>(time).count());
313     Trace::Instance().AddEntry(entry);
314 }
315 
316 void InitTrace()
317 {
318     Trace::Init();
319     TraceTable::Init();
320 }
321 
322 void DoneTrace()
323 {
324     TraceTable::Done();
325     Trace::Done();
326 }
327 
328 Tracer::Tracer(int32_t traceFunctionId_) : traceFunctionId(traceFunctionId_)
329 {
330     AddTraceEntry(EntryKind::begintraceFunctionId);
331 }
332 
333 Tracer::~Tracer()
334 {
335     AddTraceEntry(EntryKind::endtraceFunctionId);
336 }
337 
338 } } // namespace soulng::util