ec7f9211747d3331dc823576cc6ecb77531ecbcd
[platform/core/ml/nnfw.git] / runtime / onert / core / src / util / EventRecorder.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "util/EventRecorder.h"
18
19 #include <sstream>
20 #include <vector>
21 #include <unordered_map>
22 #include <json/json.h>
23 #include <assert.h>
24
25 namespace
26 {
27
28 std::string quote(const std::string &value)
29 {
30   std::stringstream ss;
31   ss << '"' << value << '"';
32   return ss.str();
33 }
34
35 std::string field(const std::string &k, const std::string &v)
36 {
37   std::stringstream ss;
38   ss << quote(k) << " : " << quote(v);
39   return ss.str();
40 }
41
42 struct Content // One Entry in Chrome Event Trace
43 {
44   std::vector<std::pair<std::string, std::string>> flds;
45   std::vector<std::pair<std::string, std::string>> args;
46 };
47
48 std::string object(const Content &content)
49 {
50   std::stringstream ss;
51
52   ss << "{ ";
53
54   ss << field(content.flds[0].first, content.flds[0].second);
55
56   for (uint32_t n = 1; n < content.flds.size(); ++n)
57   {
58     ss << ", " << field(content.flds.at(n).first, content.flds.at(n).second);
59   }
60
61   if (content.args.size() > 0)
62   {
63     ss << ", " << quote("args") << " : { ";
64     ss << field(content.args.at(0).first, content.args.at(0).second);
65
66     for (uint32_t n = 1; n < content.args.size(); ++n)
67     {
68       ss << ", " << field(content.args.at(n).first, content.args.at(n).second);
69     }
70
71     ss << "}";
72   }
73
74   ss << " }";
75
76   return ss.str();
77 }
78
79 void fill(Content &content, const Event &evt)
80 {
81   content.flds.emplace_back("name", evt.name);
82   content.flds.emplace_back("pid", "0");
83   content.flds.emplace_back("tid", evt.tid);
84   content.flds.emplace_back("ph", evt.ph);
85   content.flds.emplace_back("ts", evt.ts);
86 }
87
88 std::string object(const DurationEvent &evt)
89 {
90   Content content;
91
92   fill(content, evt);
93
94   return ::object(content);
95 }
96
97 std::string object(const CounterEvent &evt)
98 {
99   Content content;
100
101   fill(content, evt);
102
103   for (auto it = evt.values.begin(); it != evt.values.end(); ++it)
104   {
105     content.args.emplace_back(it->first, it->second);
106   }
107
108   return ::object(content);
109 }
110
111 } // namespace
112
113 void EventRecorder::emit(const DurationEvent &evt)
114 {
115   std::lock_guard<std::mutex> lock{_mu};
116
117   _duration_events.push_back(evt);
118 }
119
120 void EventRecorder::emit(const CounterEvent &evt)
121 {
122   std::lock_guard<std::mutex> lock{_mu};
123
124   _counter_events.push_back(evt);
125 }
126
127 void EventRecorder::writeToFile(std::ostream &os)
128 {
129   std::lock_guard<std::mutex> lock{_mu};
130
131   switch (_write_format)
132   {
133     case WriteFormat::CHROME_TRACING:
134       writeChromeTrace(os);
135       break;
136     case WriteFormat::SNPE_BENCHMARK:
137       writeSNPEBenchmark(os);
138       break;
139     default:
140       assert(!"Invalid value");
141       break;
142   }
143 }
144
145 void EventRecorder::writeSNPEBenchmark(std::ostream &os)
146 {
147   Json::Value root;
148   auto &exec_data = root["Execution_Data"] = Json::Value{Json::objectValue};
149
150   struct Stat
151   {
152     uint64_t sum = 0;
153     uint64_t count = 0;
154     uint64_t max = 0;
155     uint64_t min = std::numeric_limits<uint64_t>::max();
156
157     void accumulate(uint64_t val)
158     {
159       sum += val;
160       count++;
161       max = std::max(max, val);
162       min = std::min(min, val);
163     }
164   };
165
166   // Memory
167   {
168     std::unordered_map<std::string, Stat> mem_stats;
169     for (auto &evt : _counter_events)
170     {
171       auto &mem_stat = mem_stats[evt.name];
172       uint64_t val = std::stoull(evt.values["value"]);
173       mem_stat.accumulate(val);
174     }
175
176     auto &mem = exec_data["memory"] = Json::Value{Json::objectValue};
177     for (auto &kv : mem_stats)
178     {
179       auto &key = kv.first;
180       auto &val = kv.second;
181       mem[key]["Avg_Size"] = val.sum / val.count;
182       mem[key]["Max_Size"] = val.max;
183       mem[key]["Min_Size"] = val.min;
184       mem[key]["Runtime"] = "NA";
185     }
186   }
187
188   // Operation Execution Time
189   {
190     // NOTE This assumes _duration_events is sorted by "ts" ascending
191
192     // 2D keys : stats[tid][name]
193     std::unordered_map<std::string, std::unordered_map<std::string, Stat>> stats;
194     std::unordered_map<std::string, std::unordered_map<std::string, uint64_t>> begin_timestamps;
195     for (auto &evt : _duration_events)
196     {
197       auto &stat = stats[evt.tid][evt.name];
198       auto &begin_ts = begin_timestamps[evt.tid][evt.name];
199       uint64_t timestamp = std::stoull(evt.ts);
200       if (evt.ph == "B")
201       {
202         if (begin_ts != 0)
203           throw std::runtime_error{"Invalid Data"};
204         begin_ts = timestamp;
205       }
206       else if (evt.ph == "E")
207       {
208         if (begin_ts == 0 || timestamp < begin_ts)
209           throw std::runtime_error{"Invalid Data"};
210         stat.accumulate(timestamp - begin_ts);
211         begin_ts = 0;
212       }
213       else
214         throw std::runtime_error{"Invalid Data - invalid value for \"ph\" : \"" + evt.ph + "\""};
215     }
216
217     for (auto &kv : begin_timestamps)
218       for (auto &kv2 : kv.second)
219         if (kv2.second != 0)
220           throw std::runtime_error{"Invalid Data - B and E pair does not match."};
221
222     for (auto &kv : stats)
223     {
224       auto &tid = kv.first;
225       auto &map = kv.second;
226       auto &json_tid = exec_data[tid] = Json::Value{Json::objectValue};
227       for (auto &kv : map)
228       {
229         auto &name = kv.first;
230         auto &val = kv.second;
231         json_tid[name]["Avg_Time"] = val.sum / val.count;
232         json_tid[name]["Max_Time"] = val.max;
233         json_tid[name]["Min_Time"] = val.min;
234         json_tid[name]["Runtime"] = tid;
235       }
236     }
237   }
238
239   os << root;
240 }
241
242 void EventRecorder::writeChromeTrace(std::ostream &os)
243 {
244   os << "{\n";
245   os << "  " << quote("traceEvents") << ": [\n";
246
247   for (auto &evt : _duration_events)
248   {
249     os << "    " << object(evt) << ",\n";
250   }
251
252   for (auto &evt : _counter_events)
253   {
254     os << "    " << object(evt) << ",\n";
255   }
256
257   os << "    { }\n";
258   os << "  ]\n";
259   os << "}\n";
260 }