/src/WasmEdge/include/common/statistics.h
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
3 | | |
4 | | //===-- wasmedge/common/statistics.h - Executor statistics definition -----===// |
5 | | // |
6 | | // Part of the WasmEdge Project. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | /// |
10 | | /// \file |
11 | | /// This file contains the statistics class of runtime. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | #pragma once |
15 | | |
16 | | #include "common/configure.h" |
17 | | #include "common/enum_ast.hpp" |
18 | | #include "common/errcode.h" |
19 | | #include "common/span.h" |
20 | | #include "common/spdlog.h" |
21 | | #include "common/timer.h" |
22 | | |
23 | | #include <atomic> |
24 | | #include <vector> |
25 | | |
26 | | namespace WasmEdge { |
27 | | namespace Statistics { |
28 | | |
29 | | class Statistics { |
30 | | public: |
31 | | Statistics(const uint64_t Lim = UINT64_MAX) |
32 | 0 | : CostTab(UINT16_MAX + 1, 1ULL), InstrCnt(0), CostLimit(Lim), CostSum(0) { |
33 | 0 | } |
34 | | Statistics(Span<const uint64_t> Tab, const uint64_t Lim = UINT64_MAX) |
35 | | : CostTab(Tab.begin(), Tab.end()), InstrCnt(0), CostLimit(Lim), |
36 | 0 | CostSum(0) { |
37 | 0 | if (CostTab.size() < UINT16_MAX + 1) { |
38 | 0 | CostTab.resize(UINT16_MAX + 1, 0ULL); |
39 | 0 | } |
40 | 0 | } |
41 | 0 | ~Statistics() = default; |
42 | | |
43 | | /// Increment of instruction counter. |
44 | 0 | void incInstrCount() { InstrCnt.fetch_add(1, std::memory_order_relaxed); } |
45 | | |
46 | | /// Getter of instruction counter. |
47 | 0 | uint64_t getInstrCount() const { |
48 | 0 | return InstrCnt.load(std::memory_order_relaxed); |
49 | 0 | } |
50 | 0 | std::atomic_uint64_t &getInstrCountRef() { return InstrCnt; } |
51 | | |
52 | | /// Getter of instruction per second. |
53 | 0 | double getInstrPerSecond() const { |
54 | 0 | return static_cast<double>(InstrCnt) / |
55 | 0 | std::chrono::duration<double>(getWasmExecTime()).count(); |
56 | 0 | } |
57 | | |
58 | | /// Setter and setter of cost table. |
59 | 0 | void setCostTable(Span<const uint64_t> NewTable) { |
60 | 0 | CostTab.assign(NewTable.begin(), NewTable.end()); |
61 | 0 | if (unlikely(CostTab.size() < UINT16_MAX + 1)) { |
62 | 0 | CostTab.resize(UINT16_MAX + 1, 0ULL); |
63 | 0 | } |
64 | 0 | } |
65 | 0 | Span<const uint64_t> getCostTable() const noexcept { return CostTab; } |
66 | 0 | Span<uint64_t> getCostTable() noexcept { return CostTab; } |
67 | | |
68 | | /// Adder of instruction costs. |
69 | 0 | bool addInstrCost(OpCode Code) { return addCost(CostTab[uint16_t(Code)]); } |
70 | | |
71 | | /// Subber of instruction costs. |
72 | 0 | bool subInstrCost(OpCode Code) { return subCost(CostTab[uint16_t(Code)]); } |
73 | | |
74 | | /// Getter of total gas cost. |
75 | 0 | uint64_t getTotalCost() const { |
76 | 0 | return CostSum.load(std::memory_order_relaxed); |
77 | 0 | } |
78 | 0 | std::atomic_uint64_t &getTotalCostRef() { return CostSum; } |
79 | | |
80 | | /// Getter and setter of cost limit. |
81 | 0 | void setCostLimit(uint64_t Lim) { CostLimit = Lim; } |
82 | 0 | uint64_t getCostLimit() const { return CostLimit; } |
83 | | |
84 | | /// Add cost and return false if exceeded limit. |
85 | 0 | bool addCost(uint64_t Cost) { |
86 | 0 | using namespace std::literals; |
87 | 0 | const auto Limit = CostLimit; |
88 | 0 | uint64_t OldCostSum = CostSum.load(std::memory_order_relaxed); |
89 | 0 | uint64_t NewCostSum; |
90 | 0 | do { |
91 | 0 | NewCostSum = OldCostSum + Cost; |
92 | 0 | if (unlikely(NewCostSum > Limit)) { |
93 | 0 | spdlog::error("Cost exceeded limit. Force terminate the execution."sv); |
94 | 0 | return false; |
95 | 0 | } |
96 | 0 | } while (!CostSum.compare_exchange_weak(OldCostSum, NewCostSum, |
97 | 0 | std::memory_order_relaxed)); |
98 | 0 | return true; |
99 | 0 | } |
100 | | |
101 | | /// Return cost back. |
102 | 0 | bool subCost(uint64_t Cost) { |
103 | 0 | uint64_t OldCostSum = CostSum.load(std::memory_order_relaxed); |
104 | 0 | uint64_t NewCostSum; |
105 | 0 | do { |
106 | 0 | if (unlikely(OldCostSum <= Cost)) { |
107 | 0 | return false; |
108 | 0 | } |
109 | 0 | NewCostSum = OldCostSum - Cost; |
110 | 0 | } while (!CostSum.compare_exchange_weak(OldCostSum, NewCostSum, |
111 | 0 | std::memory_order_relaxed)); |
112 | 0 | return true; |
113 | 0 | } |
114 | | |
115 | | /// Clear measurement data for instructions. |
116 | 0 | void clear() noexcept { |
117 | 0 | TimeRecorder.reset(); |
118 | 0 | InstrCnt.store(0, std::memory_order_relaxed); |
119 | 0 | CostSum.store(0, std::memory_order_relaxed); |
120 | 0 | } |
121 | | |
122 | | /// Start recording wasm time. |
123 | 0 | void startRecordWasm() noexcept { |
124 | 0 | TimeRecorder.startRecord(Timer::TimerTag::Wasm); |
125 | 0 | } |
126 | | |
127 | | /// Stop recording wasm time. |
128 | 0 | void stopRecordWasm() noexcept { |
129 | 0 | TimeRecorder.stopRecord(Timer::TimerTag::Wasm); |
130 | 0 | } |
131 | | |
132 | | /// Start recording host function time. |
133 | 0 | void startRecordHost() noexcept { |
134 | 0 | TimeRecorder.startRecord(Timer::TimerTag::HostFunc); |
135 | 0 | } |
136 | | |
137 | | /// Stop recording host function time. |
138 | 0 | void stopRecordHost() noexcept { |
139 | 0 | TimeRecorder.stopRecord(Timer::TimerTag::HostFunc); |
140 | 0 | } |
141 | | |
142 | | /// Getter of execution time. |
143 | 0 | Timer::Timer::Clock::duration getWasmExecTime() const noexcept { |
144 | 0 | return TimeRecorder.getRecord(Timer::TimerTag::Wasm); |
145 | 0 | } |
146 | 0 | Timer::Timer::Clock::duration getHostFuncExecTime() const noexcept { |
147 | 0 | return TimeRecorder.getRecord(Timer::TimerTag::HostFunc); |
148 | 0 | } |
149 | 0 | Timer::Timer::Clock::duration getTotalExecTime() const noexcept { |
150 | 0 | return TimeRecorder.getRecord(Timer::TimerTag::Wasm) + |
151 | 0 | TimeRecorder.getRecord(Timer::TimerTag::HostFunc); |
152 | 0 | } |
153 | | |
154 | 0 | void dumpToLog(const Configure &Conf) const noexcept { |
155 | 0 | using namespace std::literals; |
156 | 0 | auto Nano = [](auto &&Duration) { |
157 | 0 | return std::chrono::nanoseconds(Duration).count(); |
158 | 0 | }; |
159 | 0 | const auto &StatConf = Conf.getStatisticsConfigure(); |
160 | 0 | if (StatConf.isTimeMeasuring() || StatConf.isInstructionCounting() || |
161 | 0 | StatConf.isCostMeasuring()) { |
162 | 0 | spdlog::info("==================== Statistics ===================="sv); |
163 | 0 | } |
164 | 0 | if (StatConf.isTimeMeasuring()) { |
165 | 0 | spdlog::info(" Total execution time: {} ns"sv, Nano(getTotalExecTime())); |
166 | 0 | spdlog::info(" Wasm instructions execution time: {} ns"sv, |
167 | 0 | Nano(getWasmExecTime())); |
168 | 0 | spdlog::info(" Host functions execution time: {} ns"sv, |
169 | 0 | Nano(getHostFuncExecTime())); |
170 | 0 | } |
171 | 0 | if (StatConf.isInstructionCounting()) { |
172 | 0 | spdlog::info(" Executed wasm instructions count: {}"sv, getInstrCount()); |
173 | 0 | } |
174 | 0 | if (StatConf.isCostMeasuring()) { |
175 | 0 | spdlog::info(" Gas costs: {}"sv, getTotalCost()); |
176 | 0 | } |
177 | 0 | if (StatConf.isInstructionCounting() && StatConf.isTimeMeasuring()) { |
178 | 0 | spdlog::info(" Instructions per second: {}"sv, |
179 | 0 | static_cast<uint64_t>(getInstrPerSecond())); |
180 | 0 | } |
181 | 0 | if (StatConf.isTimeMeasuring() || StatConf.isInstructionCounting() || |
182 | 0 | StatConf.isCostMeasuring()) { |
183 | 0 | spdlog::info("======================= End ======================"sv); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | private: |
188 | | std::vector<uint64_t> CostTab; |
189 | | std::atomic_uint64_t InstrCnt; |
190 | | uint64_t CostLimit; |
191 | | std::atomic_uint64_t CostSum; |
192 | | Timer::Timer TimeRecorder; |
193 | | }; |
194 | | |
195 | | } // namespace Statistics |
196 | | } // namespace WasmEdge |