Skip to content

Commit 6b3810d

Browse files
Add error handling
1 parent fe661c0 commit 6b3810d

4 files changed

Lines changed: 85 additions & 20 deletions

File tree

cli/main.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include <vector>
55

66
#include "cpuscope_lib.hpp"
7+
#include "perf_event.hpp"
8+
9+
using namespace cpuscope;
710

811
int main(int argc, char* argv[])
912
{
@@ -17,5 +20,29 @@ int main(int argc, char* argv[])
1720
}
1821

1922
std::cout << cpuscope::format_message(args) << '\n';
23+
24+
PerfEvent::LinuxSysCalls sysCalls;
25+
PerfEvent::Config config;
26+
config.scope = PerfEvent::Scope::CPU;
27+
config.cpu = 1;
28+
config.event = PerfEvent::Event::CPU_CYCLES;
29+
30+
auto perfEventOpt = PerfEvent::open(config, sysCalls);
31+
if (perfEventOpt.has_error() || !perfEventOpt.event.has_value())
32+
{
33+
std::cerr << "Error: " << perfEventOpt.error.message << " " << perfEventOpt.error.code << '\n';
34+
return 1;
35+
}
36+
37+
auto perfEvent = std::move(perfEventOpt.event.value());
38+
auto counterOpt = perfEvent.read_counter();
39+
if (!counterOpt)
40+
{
41+
std::cerr << "Failed to read perf event counter\n";
42+
return 1;
43+
}
44+
45+
std::cout << "Counter value: " << counterOpt.value() << '\n';
46+
2047
return 0;
2148
}

lib/include/perf_event.hpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55

66
#include <cstdint>
77
#include <optional>
8+
#include <system_error>
9+
10+
namespace cpuscope
11+
{
12+
13+
struct PerfEventOpenResult;
814

915
class PerfEvent
1016
{
@@ -53,12 +59,31 @@ class PerfEvent
5359
PerfEvent(PerfEvent&& other) noexcept;
5460
PerfEvent& operator=(PerfEvent&& other) noexcept;
5561

56-
static std::optional<PerfEvent> open(const Config& config, ISysCalls& sysCalls) noexcept;
62+
static PerfEventOpenResult open(const Config& config, ISysCalls& sysCalls) noexcept;
5763
std::optional<uint64_t> read_counter() noexcept;
5864

5965
private:
6066
PerfEvent(const int file_descriptor, ISysCalls& sysCalls) noexcept;
6167

6268
int m_file_descriptor = -1;
6369
ISysCalls& m_sysCalls;
64-
};
70+
};
71+
72+
struct PerfEventError
73+
{
74+
int code;
75+
std::string message;
76+
};
77+
78+
struct PerfEventOpenResult
79+
{
80+
std::optional<PerfEvent> event;
81+
PerfEventError error;
82+
83+
bool has_error() const noexcept
84+
{
85+
return error.code != 0;
86+
}
87+
};
88+
89+
} // namespace cpuscope

lib/src/perf_event.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#include <cstring>
1010
#include <optional>
1111

12+
namespace cpuscope
13+
{
14+
1215
PerfEvent::PerfEvent(const int file_descriptor, ISysCalls& sysCalls) noexcept : m_file_descriptor(file_descriptor), m_sysCalls(sysCalls) {}
1316

1417
PerfEvent::~PerfEvent() noexcept
@@ -41,11 +44,11 @@ PerfEvent& PerfEvent::operator=(PerfEvent&& other) noexcept
4144
return *this;
4245
}
4346

44-
std::optional<PerfEvent> PerfEvent::open(const Config& config, ISysCalls& sysCalls) noexcept
47+
PerfEventOpenResult PerfEvent::open(const Config& config, ISysCalls& sysCalls) noexcept
4548
{
4649
if ((config.scope == Scope::CPU && config.cpu < 0) || (config.scope == Scope::Process && config.pid < 0))
4750
{
48-
return std::nullopt;
51+
return PerfEventOpenResult{std::nullopt, PerfEventError{EINVAL, "Invalid configuration: CPU or PID must be set based on the scope"}};
4952
}
5053

5154
const pid_t pid = Scope::CPU == config.scope ? -1 : config.pid;
@@ -72,10 +75,10 @@ std::optional<PerfEvent> PerfEvent::open(const Config& config, ISysCalls& sysCal
7275
const auto file_descriptor = sysCalls.perf_event_open(&attr, pid, cpu, -1, 0);
7376
if (file_descriptor < 0)
7477
{
75-
return std::nullopt;
78+
return PerfEventOpenResult{std::nullopt, PerfEventError{errno, "Failed to open perf event"}};
7679
}
7780

78-
return std::make_optional<PerfEvent>(PerfEvent(file_descriptor, sysCalls));
81+
return PerfEventOpenResult{std::make_optional<PerfEvent>(PerfEvent(file_descriptor, sysCalls)), PerfEventError{0, ""}};
7982
}
8083

8184
std::optional<uint64_t> PerfEvent::read_counter() noexcept
@@ -110,3 +113,5 @@ ssize_t PerfEvent::LinuxSysCalls::read(int file_descriptor, void* buf, size_t co
110113
{
111114
return ::read(file_descriptor, buf, count);
112115
}
116+
117+
} // namespace cpuscope

tests/test_perf_event.cpp

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
#include "perf_event.hpp"
1212

13+
using namespace cpuscope;
14+
1315
// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members,misc-non-private-member-variables-in-classes)
1416
MATCHER_P(PerfAttrEq, expected, "")
1517
{
@@ -64,8 +66,10 @@ TEST_F(PerfEventTest, PerfEventCPUScope)
6466
return static_cast<ssize_t>(sizeof(counterValue));
6567
}));
6668

67-
auto event = PerfEvent::open(config, m_sysCalls);
69+
auto result = PerfEvent::open(config, m_sysCalls);
70+
auto event{std::move(result.event)};
6871

72+
EXPECT_FALSE(result.has_error());
6973
EXPECT_TRUE(event.has_value());
7074

7175
auto data = event->read_counter();
@@ -86,9 +90,10 @@ TEST_F(PerfEventTest, OpenPerfEventProcessScope)
8690
EXPECT_CALL(m_sysCalls, perf_event_open(PerfAttrEq(m_attr), 1234, -1, -1, 0)).WillOnce(testing::Return(file_descriptor));
8791
EXPECT_CALL(m_sysCalls, close(file_descriptor)).WillOnce(testing::Return(0));
8892

89-
auto res = PerfEvent::open(config, m_sysCalls);
93+
auto result = PerfEvent::open(config, m_sysCalls);
9094

91-
EXPECT_TRUE(res);
95+
EXPECT_FALSE(result.has_error());
96+
EXPECT_TRUE(result.event.has_value());
9297
}
9398

9499
TEST_F(PerfEventTest, OpenPerfEventCacheMisses)
@@ -103,9 +108,10 @@ TEST_F(PerfEventTest, OpenPerfEventCacheMisses)
103108
EXPECT_CALL(m_sysCalls, perf_event_open(PerfAttrEq(m_attr), 1234, -1, -1, 0)).WillOnce(testing::Return(file_descriptor));
104109
EXPECT_CALL(m_sysCalls, close(file_descriptor)).WillOnce(testing::Return(0));
105110

106-
auto res = PerfEvent::open(config, m_sysCalls);
111+
auto result = PerfEvent::open(config, m_sysCalls);
107112

108-
EXPECT_TRUE(res);
113+
EXPECT_FALSE(result.has_error());
114+
EXPECT_TRUE(result.event.has_value());
109115
}
110116

111117
TEST_F(PerfEventTest, OpenPerfEventWithInvalidCPUConfig)
@@ -114,9 +120,10 @@ TEST_F(PerfEventTest, OpenPerfEventWithInvalidCPUConfig)
114120
config.cpu = -1;
115121
config.scope = PerfEvent::Scope::CPU;
116122

117-
auto res = PerfEvent::open(config, m_sysCalls);
123+
auto result = PerfEvent::open(config, m_sysCalls);
118124

119-
EXPECT_FALSE(res);
125+
EXPECT_TRUE(result.has_error());
126+
EXPECT_FALSE(result.event.has_value());
120127
}
121128

122129
TEST_F(PerfEventTest, OpenPerfEventWithInvalidProcessConfig)
@@ -125,9 +132,10 @@ TEST_F(PerfEventTest, OpenPerfEventWithInvalidProcessConfig)
125132
config.pid = -1;
126133
config.scope = PerfEvent::Scope::Process;
127134

128-
auto res = PerfEvent::open(config, m_sysCalls);
135+
auto result = PerfEvent::open(config, m_sysCalls);
129136

130-
EXPECT_FALSE(res);
137+
EXPECT_TRUE(result.has_error());
138+
EXPECT_FALSE(result.event.has_value());
131139
}
132140

133141
TEST_F(PerfEventTest, MoveCtorTransfersOwnership)
@@ -143,9 +151,9 @@ TEST_F(PerfEventTest, MoveCtorTransfersOwnership)
143151
{
144152
auto original = PerfEvent::open(config, m_sysCalls);
145153

146-
ASSERT_TRUE(original.has_value());
154+
ASSERT_TRUE(original.event.has_value());
147155

148-
const PerfEvent moved(std::move(original.value()));
156+
const PerfEvent moved(std::move(original.event.value()));
149157
}
150158
}
151159

@@ -170,10 +178,10 @@ TEST_F(PerfEventTest, MoveAssignmentTransfersOwnership)
170178
auto lhs = PerfEvent::open(config1, m_sysCalls);
171179
auto rhs = PerfEvent::open(config2, m_sysCalls);
172180

173-
ASSERT_TRUE(lhs.has_value());
174-
ASSERT_TRUE(rhs.has_value());
181+
ASSERT_TRUE(lhs.event.has_value());
182+
ASSERT_TRUE(rhs.event.has_value());
175183

176-
lhs.value() = std::move(rhs.value());
184+
lhs.event.value() = std::move(rhs.event.value());
177185
}
178186
}
179187
// NOLINTEND(bugprone-unchecked-optional-access)

0 commit comments

Comments
 (0)