1
2
3
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::string, int32_t> traceFunctionMap;
76 std::map<int32_t, std::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(traceTableFile, line))
141 {
142 std::string::size_type colonPos = line.find(':');
143 std::string indexStr = line.substr(0, colonPos);
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(winMsgFile, msgLine))
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(0, colonPos);
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(kind, threadId, id, nanosecs);
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 kind, int32_t id)
309 {
310 if (!tracing) return;
311 std::chrono::steady_clock::duration time = std::chrono::steady_clock::now() - start;
312 TraceEntry entry(kind, threadId, id, std::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::begin, traceFunctionId);
331 }
332
333 Tracer::~Tracer()
334 {
335 AddTraceEntry(EntryKind::end, traceFunctionId);
336 }
337
338 } }