Coverage Report

Created: 2025-07-01 06:18

/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