--- /dev/null
+# Enable xtrace build only when there is an explicit user request
+option(BUILD_CONTRIB_XTRACE "Build xtrace" OFF)
+
+file(GLOB_RECURSE SOURCES "src/*.cc")
+
+add_executable(xtrace ${SOURCES})
+target_link_libraries(xtrace nnfw_lib_tflite)
+target_link_libraries(xtrace nnfw_lib_misc)
+target_link_libraries(xtrace nnfw_lib_cpp14)
+target_link_libraries(xtrace nnfw_lib_xray_pipe)
+target_link_libraries(xtrace nnfw_lib_xray_mux)
+target_link_libraries(xtrace nnfw_lib_xdata)
+target_link_libraries(xtrace ${LIB_PTHREAD})
+target_link_libraries(xtrace dl)
+
+install(TARGETS xtrace DESTINATION bin)
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark_event.h"
+
+#include <xray/pipe.h>
+
+xray::event_code BMCategory::set(std::unique_ptr<BMEvent> &&event)
+{
+ _event = std::move(event);
+ return xray::event_code{0};
+}
+
+void BMCategory::reset(void) { _event.reset(); }
+
+void BMCategory::post(std::unique_ptr<BMEvent> &&evt_info)
+{
+ auto evt_cat = this;
+ auto evt_idx = set(std::move(evt_info));
+ const xray::event evt{evt_cat, evt_idx};
+ xray::pipe::post(&evt);
+ reset();
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BENCHMARK_EVENT_H__
+#define __BENCHMARK_EVENT_H__
+
+#include <xray/event.h>
+#include <xray/event_code.h>
+#include <xray/event_category.h>
+
+#include <chrono>
+#include <memory>
+
+enum BMPhase
+{
+ Warmup,
+ Stable
+};
+
+struct BMEvent
+{
+ virtual ~BMEvent() = default;
+};
+
+struct BMBegin : public BMEvent
+{
+ BMPhase phase;
+ uint32_t cur_iter;
+};
+
+struct BMEnd : public BMEvent
+{
+ BMPhase phase;
+ uint32_t cur_iter;
+ std::chrono::milliseconds elapsed;
+};
+
+class BMCategory final : public xray::event_category
+{
+private:
+ BMCategory() = default;
+
+public:
+ xray::event_code set(std::unique_ptr<BMEvent> &&event);
+ void reset(void);
+
+public:
+ const BMEvent *event(void) const { return _event.get(); }
+
+private:
+ std::unique_ptr<BMEvent> _event;
+
+public:
+ static BMCategory *get(void)
+ {
+ static BMCategory cat;
+ return &cat;
+ }
+
+public:
+ void post(std::unique_ptr<BMEvent> &&event);
+};
+
+#endif // __BENCHMARK_EVENT_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark_runner.h"
+#include "benchmark_event.h"
+
+#include <tensorflow/lite/model.h>
+
+#include <tflite/ext/kernels/register.h>
+#include <tflite/Assert.h>
+#include <tflite/NNAPISession.h>
+
+#include <misc/environment.h>
+#include <misc/benchmark.h>
+
+#include <cpp14/memory.h>
+
+#include <iostream>
+
+using namespace tflite;
+using namespace nnfw::tflite;
+using namespace std::chrono;
+
+namespace
+{
+
+void notify(const BMBegin &event)
+{
+ BMCategory::get()->post(nnfw::cpp14::make_unique<BMBegin>(event));
+}
+
+void notify(const BMEnd &event) { BMCategory::get()->post(nnfw::cpp14::make_unique<BMEnd>(event)); }
+
+} // namespace
+
+void BMRunner<TFL_NNAPI_DELEGATE>::run(const std::string &filename) const
+{
+ BuiltinOpResolver op_resolver;
+ StderrReporter error_reporter;
+
+ auto model = FlatBufferModel::BuildFromFile(filename.c_str(), &error_reporter);
+
+ if (model == nullptr)
+ {
+ throw std::runtime_error{"Cannot create model"};
+ }
+
+ InterpreterBuilder builder(*model, op_resolver);
+
+ std::unique_ptr<Interpreter> interp;
+ TFLITE_ENSURE(builder(&interp));
+
+ auto sess = std::make_shared<nnfw::tflite::NNAPISession>(interp.release());
+
+ // Iteration!
+ for (auto phase : {Warmup, Stable})
+ {
+ auto get_iteration_count = [](const BMPhase &phase) {
+ switch (phase)
+ {
+ case Warmup:
+ return 1; // Allow configuration
+ case Stable:
+ return 3;
+ default:
+ break;
+ }
+
+ throw std::runtime_error{"Error!"};
+ };
+
+ uint32_t iteration_count = get_iteration_count(phase);
+
+ for (uint32_t n = 0; n < iteration_count; ++n)
+ {
+ // Notify event
+ {
+ BMBegin event;
+
+ event.phase = phase;
+ event.cur_iter = n;
+
+ notify(event);
+ }
+
+ sess->prepare();
+
+ std::chrono::milliseconds elapsed(0);
+ nnfw::misc::benchmark::measure(elapsed) << [&](void) {
+ if (!sess->run())
+ {
+ throw std::runtime_error{"run failed"};
+ }
+ };
+
+ sess->teardown();
+
+ // Notify
+ {
+ BMEnd event;
+
+ event.phase = phase;
+ event.cur_iter = n;
+ event.elapsed = elapsed;
+
+ notify(event);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BENCHMARK_RUNNER_H__
+#define __BENCHMAKR_RUNNER_H__
+
+#include <string>
+
+using TFLModelPath = std::string;
+
+enum BMRunnerType
+{
+ // Use T/F Lite interpreter with Android NN API Delegate
+ TFL_NNAPI_DELEGATE
+};
+
+template <BMRunnerType E> struct BMRunner;
+
+template <> struct BMRunner<TFL_NNAPI_DELEGATE>
+{
+ void run(const TFLModelPath &filename) const;
+};
+
+#endif // __BENCHMARK_RUNNER_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "event_collector.h"
+#include "benchmark_event.h"
+
+// xtrace-internal libraries
+#include "str.h"
+
+// NNFW-internal libraries
+#include <xdata.h>
+#include <cpp14/memory.h>
+
+// C++ standard libraries
+#include <chrono>
+#include <iostream>
+
+// POSIX standard libraries
+#include <sys/time.h>
+#include <sys/resource.h>
+
+using nnfw::cpp14::make_unique;
+
+namespace
+{
+
+std::string timestamp(void)
+{
+ auto now = std::chrono::steady_clock::now();
+ return std::to_string(
+ std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count());
+}
+
+class DurationEventBuilder
+{
+public:
+ DurationEventBuilder(const std::string &ts) : _ts{ts} {}
+
+ DurationEvent build(const std::string &name, const std::string &ph) const
+ {
+ DurationEvent evt;
+
+ evt.name = name;
+ evt.ph = ph;
+ evt.ts = _ts;
+
+ return evt;
+ }
+
+private:
+ std::string _ts;
+};
+
+void emit_rusage(EventRecorder *rec, const std::string &ts)
+{
+ struct rusage ru;
+
+ getrusage(RUSAGE_SELF, &ru);
+ {
+ CounterEvent evt;
+
+ evt.name = "maxrss";
+ evt.ph = "C";
+ evt.ts = ts;
+ evt.values["value"] = std::to_string(ru.ru_maxrss);
+
+ rec->emit(evt);
+ }
+
+ {
+ CounterEvent evt;
+
+ evt.name = "minflt";
+ evt.ph = "C";
+ evt.ts = ts;
+ evt.values["value"] = std::to_string(ru.ru_minflt);
+
+ rec->emit(evt);
+ }
+}
+
+std::ostream &operator<<(std::ostream &os, const BMPhase &phase)
+{
+ os << ((phase == Warmup) ? "Warmup" : "Iteration");
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const std::chrono::milliseconds &dur)
+{
+ os << dur.count();
+ return os;
+}
+
+} // namespace
+
+void EventCollector::notify(const xray::event *e)
+{
+ auto ts = timestamp();
+
+ // Record trace events (region enter/leave)
+ if (e->cat() == xdata::trace::category::get())
+ {
+ auto info = xdata::trace::category::get()->info();
+
+ switch (info->action())
+ {
+ case xdata::trace::enter:
+ _rec->emit(DurationEventBuilder(ts).build(info->region()->name(), "B"));
+ break;
+
+ case xdata::trace::leave:
+ _rec->emit(DurationEventBuilder(ts).build(info->region()->name(), "E"));
+ break;
+ }
+ }
+
+ // Record benchmark events
+ if (e->cat() == BMCategory::get())
+ {
+ auto make_head = [](const BMPhase &phase, uint32_t iter) { return str(phase, " ", iter); };
+
+ if (auto info = dynamic_cast<const BMBegin *>(BMCategory::get()->event()))
+ {
+ auto name = str(info->phase, info->cur_iter);
+ _rec->emit(DurationEventBuilder(ts).build(name, "B"));
+
+ auto head = make_head(info->phase, info->cur_iter);
+ std::cout << head << std::endl;
+ }
+
+ if (auto info = dynamic_cast<const BMEnd *>(BMCategory::get()->event()))
+ {
+ auto name = str(info->phase, info->cur_iter);
+ _rec->emit(DurationEventBuilder(ts).build(name, "E"));
+
+ auto head = make_head(info->phase, info->cur_iter);
+ std::cout << head << " - done " << std::endl;
+ std::cout << head << " takes " << info->elapsed << "ms" << std::endl;
+ }
+ }
+
+ // Trace resource usage per each event notification
+ emit_rusage(_rec, ts);
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EVENT_COLLECTOR_H__
+#define __EVENT_COLLECTOR_H__
+
+#include "event_recorder.h"
+
+#include <xray/mux.h>
+
+class EventCollector final : public xray::listener
+{
+public:
+ EventCollector(EventRecorder *rec) : _rec{rec}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void notify(const xray::event *e) final;
+
+private:
+ EventRecorder *_rec = nullptr;
+};
+
+#endif // __EVENT_COLLECTOR_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "event_recorder.h"
+#include "str.h"
+
+#include <vector>
+
+namespace
+{
+
+std::string quote(const std::string &value)
+{
+ std::stringstream ss;
+ ss << '"' << value << '"';
+ return ss.str();
+}
+
+std::string field(const std::string &k, const std::string &v)
+{
+ std::stringstream ss;
+ ss << quote(k) << " : " << quote(v);
+ return ss.str();
+}
+
+struct Content // One Entry in Chrome Event Trace
+{
+ std::vector<std::pair<std::string, std::string>> flds;
+ std::vector<std::pair<std::string, std::string>> args;
+};
+
+std::string object(const Content &content)
+{
+ std::stringstream ss;
+
+ ss << "{ ";
+
+ ss << field(content.flds[0].first, content.flds[0].second);
+
+ for (uint32_t n = 1; n < content.flds.size(); ++n)
+ {
+ ss << ", " << field(content.flds.at(n).first, content.flds.at(n).second);
+ }
+
+ if (content.args.size() > 0)
+ {
+ ss << ", " << quote("args") << " : { ";
+ ss << field(content.args.at(0).first, content.args.at(0).second);
+
+ for (uint32_t n = 1; n < content.args.size(); ++n)
+ {
+ ss << ", " << field(content.args.at(n).first, content.args.at(n).second);
+ }
+
+ ss << "}";
+ }
+
+ ss << " }";
+
+ return ss.str();
+}
+
+void fill(Content &content, const Event &evt)
+{
+ content.flds.emplace_back("name", evt.name);
+ content.flds.emplace_back("pid", "0");
+ content.flds.emplace_back("tid", "0");
+ content.flds.emplace_back("ph", evt.ph);
+ content.flds.emplace_back("ts", evt.ts);
+}
+
+std::string object(const DurationEvent &evt)
+{
+ Content content;
+
+ fill(content, evt);
+
+ return ::object(content);
+}
+
+std::string object(const CounterEvent &evt)
+{
+ Content content;
+
+ fill(content, evt);
+
+ for (auto it = evt.values.begin(); it != evt.values.end(); ++it)
+ {
+ content.args.emplace_back(it->first, it->second);
+ }
+
+ return ::object(content);
+}
+
+} // namespace
+
+void EventRecorder::init()
+{
+ _os << "{" << std::endl;
+ _os << " " << quote("traceEvents") << ": [" << std::endl;
+}
+
+void EventRecorder::emit(const DurationEvent &evt)
+{
+ _os << " " << object(evt) << "," << std::endl;
+}
+void EventRecorder::emit(const CounterEvent &evt)
+{
+ _os << " " << object(evt) << "," << std::endl;
+}
+
+void EventRecorder::fini()
+{
+ _os << " { }" << std::endl;
+ _os << " ]" << std::endl;
+ _os << "}" << std::endl;
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EVENT_RECORDER_H__
+#define __EVENT_RECORDER_H__
+
+#include <map>
+#include <memory>
+
+#include <ostream>
+
+struct Event
+{
+ std::string name;
+ std::string ph; /* REQUIRED */
+ std::string ts; /* REQUIRED */
+};
+
+struct DurationEvent : public Event
+{
+ // TO BE FILLED
+};
+
+struct CounterEvent : public Event
+{
+ std::map<std::string, std::string> values;
+};
+
+//
+// Record Event as Chrome Trace Event File Format
+//
+// Refrence: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
+//
+class EventRecorder
+{
+public:
+ EventRecorder(std::ostream &os) : _os(os)
+ {
+ // DO NOTHING
+ }
+
+public:
+ void init();
+
+public:
+ void emit(const DurationEvent &evt);
+ void emit(const CounterEvent &evt);
+
+public:
+ void fini();
+
+private:
+ std::ostream &_os;
+};
+
+#endif // __EVENT_RECORDER_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STR_H__
+#define __STR_H__
+
+#include <ostream>
+#include <sstream>
+
+template <typename Arg> void _str(std::ostream &os, Arg &&arg) { os << std::forward<Arg>(arg); }
+
+template <typename Arg, typename... Args> void _str(std::ostream &os, Arg &&arg, Args &&... args)
+{
+ _str(os, std::forward<Arg>(arg));
+ _str(os, std::forward<Args>(args)...);
+}
+
+template <typename... Args> std::string str(Args &&... args)
+{
+ std::stringstream ss;
+ _str(ss, std::forward<Args>(args)...);
+ return ss.str();
+}
+
+#endif // __STR_H__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "event_recorder.h"
+#include "event_collector.h"
+#include "benchmark_runner.h"
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+// xtrace --out <output path> <T/F Lite model path)
+static int entry(int argc, char **argv)
+{
+ assert(argc == 4);
+ assert(std::string(argv[1]) == "--out");
+
+ // Create a file
+ std::ofstream ofs{argv[2], std::ofstream::out};
+
+ // Create an event recorder
+ EventRecorder recorder{ofs};
+
+ recorder.init();
+
+ EventCollector event_collector{&recorder};
+
+ xray::mux::get().attach(&event_collector);
+
+ BMRunner<TFL_NNAPI_DELEGATE>().run(argv[3]);
+
+ xray::mux::get().detach(&event_collector);
+
+ recorder.fini();
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ try
+ {
+ return entry(argc, argv);
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << e.what() << std::endl;
+ return 255;
+ }
+}