Add code to parse /proc/interrupts (and tests)
This sets us up to configure the interrupts automatically
Change-Id: Ibefb3e1b02badc3f14abf14e49333c9a8ed987f6
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/aos/starter/BUILD b/aos/starter/BUILD
index d4566a0..5990c61 100644
--- a/aos/starter/BUILD
+++ b/aos/starter/BUILD
@@ -201,3 +201,23 @@
"//aos/events/logging:logger_main",
],
)
+
+cc_library(
+ name = "irq_affinity_lib",
+ srcs = ["irq_affinity_lib.cc"],
+ hdrs = ["irq_affinity_lib.h"],
+ deps = [
+ "//aos/scoped:scoped_fd",
+ "@com_github_google_glog//:glog",
+ "@com_google_absl//absl/strings",
+ ],
+)
+
+cc_test(
+ name = "irq_affinity_lib_test",
+ srcs = ["irq_affinity_lib_test.cc"],
+ deps = [
+ ":irq_affinity_lib",
+ "//aos/testing:googletest",
+ ],
+)
diff --git a/aos/starter/irq_affinity_lib.cc b/aos/starter/irq_affinity_lib.cc
new file mode 100644
index 0000000..ccc4853
--- /dev/null
+++ b/aos/starter/irq_affinity_lib.cc
@@ -0,0 +1,288 @@
+#include "aos/starter/irq_affinity_lib.h"
+
+#include <fcntl.h>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_split.h"
+#include "aos/scoped/scoped_fd.h"
+
+namespace aos {
+
+// Class to split strings by whitespace with absl::StrSplit.
+class ByWhitespace {
+ public:
+ // Returns the location of the next separator per the StrSplit API.
+ absl::string_view Find(absl::string_view text, size_t pos) const {
+ size_t count = 0;
+ const char *start = text.data() + text.size();
+ for (size_t i = pos; i < text.size(); ++i) {
+ if (text[i] == ' ') {
+ if (count == 0) {
+ start = text.data() + i;
+ }
+ ++count;
+ } else {
+ if (count > 0) {
+ break;
+ }
+ }
+ }
+ return std::string_view(start, count);
+ }
+};
+
+InterruptsStatus::InterruptsStatus() {
+ // 8k seems to be big enough to hold most things, so let's start there.
+ interrupts_content_.reserve(8192);
+}
+
+namespace {
+
+// Counts the number of "CPU" strings in the line without allocating.
+size_t CountCores(std::string_view line) {
+ size_t cpus_found = 0;
+ std::string_view::size_type start_pos = 0;
+ while (std::string_view::npos != (start_pos = line.find("CPU", start_pos))) {
+ start_pos += 3;
+ ++cpus_found;
+ }
+ return cpus_found;
+}
+
+} // namespace
+
+void InterruptsStatus::Update(std::string_view contents) {
+ size_t line_number = 0;
+ for (std::string_view line :
+ absl::StrSplit(contents, "\n", absl::SkipEmpty())) {
+ if (line_number == 0) {
+ // This is the CPUs line. Count our cores.
+ const size_t cpus_found = CountCores(line);
+
+ if (cpus_ == 0) {
+ // First time through.
+ cpus_ = cpus_found;
+ } else {
+ CHECK_EQ(cpus_found, cpus_) << ": Number of CPUs changed while running";
+ }
+ } else {
+ size_t element_number = 0;
+ InterruptState *state = nullptr;
+ bool new_element = false;
+ for (const std::string_view element :
+ absl::StrSplit(absl::StripAsciiWhitespace(line),
+ absl::MaxSplits(ByWhitespace(), cpus_ + 1))) {
+ if (element_number == 0) {
+ // Parse the interrupt number. This should either be in the form of:
+ // 23:
+ // or
+ // Err:
+ CHECK_EQ(element[element.size() - 1], ':')
+ << ": Missing trailing ':'";
+
+ int interrupt_number;
+ std::string_view interrupt_name =
+ element.substr(0, element.size() - 1);
+
+ // It's a named interrupt.
+ if (!absl::SimpleAtoi(interrupt_name, &interrupt_number)) {
+ interrupt_number = -1;
+ } else {
+ interrupt_name = "";
+ }
+
+ // Add a new element if we are too short, or if the interrupt changed.
+ if (states_.size() < line_number) {
+ new_element = true;
+ } else {
+ InterruptState *state = &states_[line_number - 1];
+ if (state->interrupt_number != interrupt_number ||
+ state->interrupt_name != interrupt_name) {
+ VLOG(1)
+ << "IRQ changed names... Blow away the end and try again.";
+ // This happens infrequently enough that it isn't worth trying to
+ // resize things. It may never happen while running. Nuke
+ // anything missing and retry.
+ states_.resize(line_number - 1);
+ new_element = true;
+ }
+ }
+
+ if (new_element) {
+ InterruptState new_state;
+ std::vector<unsigned int> irq_count(cpus_, 0);
+ states_.push_back(InterruptState{
+ .interrupt_number = interrupt_number,
+ .interrupt_name = interrupt_number == -1
+ ? std::string(interrupt_name)
+ : std::string(),
+ .count = std::move(irq_count),
+ .chip_name = std::string(),
+ .description = std::string(),
+ .hwirq = std::string(),
+ .actions = {},
+ });
+ }
+ state = &states_[line_number - 1];
+
+ } else if (element_number <= cpus_) {
+ // We are now parsing the count body. Keep updating the elements.
+ unsigned int interrupt_count;
+ CHECK(absl::SimpleAtoi(element, &interrupt_count))
+ << ": Failed to parse count " << interrupt_count;
+ state->count[element_number - 1] = interrupt_count;
+ } else if (element_number == cpus_ + 1) {
+ if (state->interrupt_number == -1) {
+ // Named interrupt, the rest of the string is the description.
+ if (new_element) {
+ state->description = std::string(element);
+ } else {
+ CHECK_EQ(state->description, element) << ": Description changed";
+ }
+ } else {
+ // Ok, the rest is now some properties of the interrupt.
+ size_t trailing_elements_count = 0;
+ for (std::string_view trailing_element :
+ absl::StrSplit(absl::StripAsciiWhitespace(element),
+ absl::MaxSplits(ByWhitespace(), 2))) {
+ if (trailing_elements_count == 0) {
+ // Chip name.
+ if (new_element) {
+ state->chip_name = std::string(trailing_element);
+ } else {
+ CHECK_EQ(state->chip_name, trailing_element)
+ << ": Chip changed names";
+ }
+ } else if (trailing_elements_count == 1) {
+ // Hardware IRQ
+ if (new_element) {
+ state->hwirq = std::string(trailing_element);
+ } else {
+ CHECK_EQ(state->hwirq, trailing_element)
+ << ": Hardware IRQ changed names";
+ }
+ } else {
+ // And then either "Level/Edge" and then the actions, or just
+ // the actions. Kernel has CONFIG_GENERIC_IRQ_SHOW_LEVEL
+ // enabled if the string starts with either.. Strip it until someone finds a use.
+ if (absl::StartsWith(trailing_element, "Level")) {
+ trailing_element =
+ absl::StripAsciiWhitespace(trailing_element.substr(5));
+ } else if (absl::StartsWith(trailing_element, "Edge")) {
+ trailing_element =
+ absl::StripAsciiWhitespace(trailing_element.substr(4));
+ }
+
+ // Split up the actions by ", " and stick them in.
+ if (new_element) {
+ state->actions = std::vector<std::string>(
+ absl::StrSplit(trailing_element, ", "));
+ } else {
+ size_t action_index = 0;
+ bool matches = true;
+ // Start by comparing. If we don't match, then set. This
+ // avoids an allocation if we can get away with it.
+ for (std::string_view action :
+ absl::StrSplit(trailing_element, ", ")) {
+ if (action_index >= state->actions.size()) {
+ matches = false;
+ break;
+ }
+ if (state->actions[action_index] != action) {
+ matches = false;
+ break;
+ }
+ ++action_index;
+ }
+ if (!matches) {
+ state->actions = std::vector<std::string>(
+ absl::StrSplit(trailing_element, ", "));
+ }
+ }
+ }
+ ++trailing_elements_count;
+ }
+ }
+ } else {
+ LOG(FATAL) << "Unexpected element, need to consume " << element;
+ }
+ ++element_number;
+ }
+
+ // Validate that everything makes sense and we have the elements expected.
+ if (state->interrupt_number != -1) {
+ CHECK_EQ(element_number, cpus_ + 2);
+ } else {
+ // Only these 3 interrupts are known to not be per core.
+ if (state->interrupt_name == "Err" || state->interrupt_name == "ERR" ||
+ state->interrupt_name == "MIS") {
+ if (new_element) {
+ CHECK_LE(element_number, cpus_ + 1);
+ state->count.resize(element_number - 1);
+ } else {
+ CHECK_EQ(state->count.size(), element_number - 1);
+ }
+ } else {
+ CHECK_EQ(element_number, cpus_ + 2);
+ }
+ }
+
+ if (VLOG_IS_ON(1)) {
+ if (state->interrupt_number == -1) {
+ LOG(INFO) << "IRQ: " << state->interrupt_name;
+ } else {
+ LOG(INFO) << "IRQ: " << state->interrupt_number;
+ }
+ for (unsigned int c : state->count) {
+ LOG(INFO) << " " << c;
+ }
+ if (!state->chip_name.empty()) {
+ LOG(INFO) << "chip_name \"" << state->chip_name << "\"";
+ }
+ if (!state->description.empty()) {
+ LOG(INFO) << "description \"" << state->description << "\"";
+ }
+ if (!state->hwirq.empty()) {
+ LOG(INFO) << "hwirq \"" << state->hwirq << "\"";
+ }
+ if (!state->actions.empty()) {
+ for (const std::string &action : state->actions) {
+ LOG(INFO) << " action \"" << action << "\"";
+ }
+ }
+ }
+ }
+
+ ++line_number;
+ }
+}
+
+void InterruptsStatus::Update() {
+ ScopedFD fd(open("/proc/interrupts", O_RDONLY));
+ size_t so_far = 0;
+ while (true) {
+ // Keep growing the size of interrupts_content_ until it holds the whole
+ // string.
+ size_t kStride = 8192;
+ if (interrupts_content_.capacity() < so_far + kStride) {
+ interrupts_content_.reserve(interrupts_content_.capacity() + kStride);
+ }
+
+ interrupts_content_.resize(interrupts_content_.capacity());
+
+ const ssize_t result = read(fd.get(), interrupts_content_.data() + so_far,
+ interrupts_content_.capacity() - so_far);
+ PCHECK(result >= 0) << ": reading from /proc/interrupts";
+ if (result == 0) {
+ break;
+ }
+ so_far += result;
+ }
+
+ interrupts_content_.resize(so_far);
+ Update(
+ std::string_view(interrupts_content_.data(), interrupts_content_.size()));
+}
+
+} // namespace aos
diff --git a/aos/starter/irq_affinity_lib.h b/aos/starter/irq_affinity_lib.h
new file mode 100644
index 0000000..ccb7d03
--- /dev/null
+++ b/aos/starter/irq_affinity_lib.h
@@ -0,0 +1,63 @@
+#ifndef AOS_STARTER_IRQ_AFFINITY_LIB_H_
+#define AOS_STARTER_IRQ_AFFINITY_LIB_H_
+
+#include <string>
+#include <vector>
+
+#include "glog/logging.h"
+
+namespace aos {
+
+// Class to parse /proc/interrupts.
+class InterruptsStatus {
+ public:
+ InterruptsStatus();
+
+ // Updates the interrupt state.
+ void Update();
+
+ // Updates the interrupt state from the contents of /proc/interrupts.
+ //
+ // This should only be used for testing.
+ void Update(std::string_view contents);
+
+ // Information about each interrupt.
+ struct InterruptState {
+ // IRQ number. -1 if this doesn't have a number.
+ int interrupt_number;
+ // Name of the interrupt. Only populated when number == -1
+ std::string interrupt_name;
+ // IRQs triggered per core, where the vector index is the core.
+ std::vector<unsigned int> count;
+
+ // The name of the irq chip controller.
+ std::string chip_name;
+
+ // Description of the IRQ if it doesn't have an interrupt number.
+ std::string description;
+
+ // Hardware IRQ "number".
+ std::string hwirq;
+
+ // List of actions. An action is something which gets triggered on an
+ // interrupt. This is the IRQ "name", and is a vector to cover shared IRQs.
+ std::vector<std::string> actions;
+ };
+
+ // Information about all IRQs.
+ const std::vector<InterruptState> &states() const { return states_; }
+
+ private:
+
+ // Buffer to hold the contents of /proc/interrupts to avoid re-allocating
+ // continually.
+ std::vector<char> interrupts_content_;
+
+ size_t cpus_ = 0;
+
+ std::vector<InterruptState> states_;
+};
+
+} // namespace aos
+
+#endif // AOS_STARTER_IRQ_AFFINITY_LIB_H_
diff --git a/aos/starter/irq_affinity_lib_test.cc b/aos/starter/irq_affinity_lib_test.cc
new file mode 100644
index 0000000..0fe240b
--- /dev/null
+++ b/aos/starter/irq_affinity_lib_test.cc
@@ -0,0 +1,278 @@
+#include "aos/starter/irq_affinity_lib.h"
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+namespace aos::testing {
+
+constexpr std::string_view kRockPiContents =
+ R"contents( CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
+ 23: 4221703 2325251 5737616 1332002 1418781 1368697 GICv3 30 Level arch_timer
+ 25: 8034134 3985740 5155495 1708244 1835948 1635570 GICv3 113 Level rk_timer
+ 31: 11137 2335 4349 978 0 0 GICv3-23 0 Level arm-pmu
+ 32: 0 0 0 0 837 820 GICv3-23 1 Level arm-pmu
+ 33: 0 0 0 0 0 0 GICv3 59 Level rockchip_usb2phy
+ 34: 0 0 0 0 0 0 GICv3 63 Level rockchip_usb2phy
+ 35: 0 0 0 0 0 0 GICv3 37 Level ff6d0000.dma-controller
+ 36: 0 0 0 0 0 0 GICv3 38 Level ff6d0000.dma-controller
+ 37: 0 0 0 0 0 0 GICv3 39 Level ff6e0000.dma-controller
+ 38: 97326477 0 0 0 0 0 GICv3 40 Level ff6e0000.dma-controller
+ 39: 240 0 0 0 0 0 GICv3 132 Level ttyS2
+ 40: 0 0 0 0 0 0 GICv3 147 Level ff650800.iommu
+ 41: 0 0 0 0 0 0 GICv3 149 Level ff660480.iommu
+ 42: 0 0 0 0 0 0 GICv3 151 Level ff8f3f00.iommu, ff8f0000.vop
+ 43: 0 0 0 0 0 0 GICv3 150 Level ff903f00.iommu, ff900000.vop
+ 44: 0 0 3412401 0 0 0 GICv3 75 Level ff914000.iommu, rkisp1
+ 45: 0 0 0 0 0 0 GICv3 76 Level ff924000.iommu
+ 46: 0 0 0 0 0 0 GICv3 85 Level ff1d0000.spi
+ 47: 0 0 0 0 0 0 GICv3 91 Level ff110000.i2c
+ 48: 0 0 0 0 0 0 GICv3 66 Level ff130000.i2c
+ 49: 264 0 1386 0 0 0 GICv3 89 Level ff3c0000.i2c
+ 50: 0 0 0 0 0 0 rockchip_gpio_irq 21 Level rk808
+ 56: 0 0 0 0 0 0 rk808 5 Edge RTC alarm
+ 60: 54 0 357 0 0 0 GICv3 88 Level ff3d0000.i2c
+ 61: 0 0 0 0 0 0 GICv3 152 Edge ff848000.watchdog
+ 62: 9491 70670 0 0 0 0 GICv3 97 Level dw-mci
+ 63: 207 0 0 0 0 0 GICv3 43 Level mmc1
+ 64: 0 0 0 0 0 0 rockchip_gpio_irq 7 Edge fe320000.mmc cd
+ 65: 0 0 0 0 0 0 GICv3 129 Level rockchip_thermal
+ 66: 0 0 0 0 0 0 GICv3 94 Level ff100000.saradc
+ 67: 0 0 0 0 0 0 GICv3 58 Level ehci_hcd:usb1
+ 68: 0 0 0 0 0 0 GICv3 62 Level ehci_hcd:usb2
+ 69: 0 0 0 0 0 0 GICv3 60 Level ohci_hcd:usb3
+ 70: 0 0 0 0 0 0 GICv3 64 Level ohci_hcd:usb4
+ 71: 37 0 0 33541 0 0 GICv3 44 Level eth0
+ 72: 0 0 0 0 0 0 GICv3 137 Level xhci-hcd:usb5
+ 73: 52061 0 0 0 0 0 GICv3 142 Level xhci-hcd:usb7
+ 74: 0 0 0 0 0 0 GICv3 87 Level ff680000.rga
+ 75: 0 0 0 0 0 0 GICv3 148 Level ff660000.video-codec
+ 76: 0 0 0 0 0 0 GICv3 146 Level ff650000.video-codec
+ 77: 0 0 0 0 0 0 GICv3 145 Level ff650000.video-codec
+ 78: 0 0 0 0 0 0 GICv3 55 Level ff940000.hdmi
+ 79: 50468710 0 0 0 0 0 rockchip_gpio_irq 16 Level adis16505
+IPI0: 629986 29367897 1604808 9830343 7634766 7528717 Rescheduling interrupts
+IPI1: 11040 79148 22290 41912 29091 21995 Function call interrupts
+IPI2: 0 0 0 0 0 0 CPU stop interrupts
+IPI3: 0 0 0 0 0 0 CPU stop (for crash dump) interrupts
+IPI4: 5391338 7612504 8510753 10479370 10766273 10712987 Timer broadcast interrupts
+IPI5: 3040632 140996 4922363 50728 31487 24500 IRQ work interrupts
+IPI6: 0 0 0 0 0 0 CPU wake-up interrupts
+Err: 0
+)contents";
+
+constexpr std::string_view kRockPiContents2 =
+ R"contents( CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
+ 23: 1703 25251 737616 2002 18781 368697 GICv3 30 Level arch_timer
+ 25: 8034134 3985740 5155495 1708244 1835948 1635570 GICv3 113 Level rk_timer
+ 31: 11137 2335 4349 978 0 0 GICv3-23 0 Level arm-pmu
+ 32: 0 0 0 0 837 820 GICv3-23 1 Level arm-pmu
+ 33: 0 0 0 0 0 0 GICv3 59 Level rockchip_usb2phy
+ 34: 0 0 0 0 0 0 GICv3 63 Level rockchip_usb2phy
+ 35: 0 0 0 0 0 0 GICv3 37 Level ff6d0000.dma-controller
+ 36: 0 0 0 0 0 0 GICv3 38 Level ff6d0000.dma-controller
+ 37: 0 0 0 0 0 0 GICv3 39 Level ff6e0000.dma-controller
+ 38: 97326477 0 0 0 0 0 GICv3 40 Level ff6e0000.dma-controller
+ 39: 240 0 0 0 0 0 GICv3 132 Level ttyS2
+ 40: 0 0 0 0 0 0 GICv3 147 Level ff650800.iommu
+ 41: 0 0 0 0 0 0 GICv3 149 Level ff660480.iommu
+ 42: 0 0 0 0 0 0 GICv3 151 Level ff8f3f00.iommu, ff8f0000.vop
+ 43: 0 0 0 0 0 0 GICv3 150 Level ff903f00.iommu, ff900000.vop
+ 44: 0 0 3412401 0 0 0 GICv3 75 Level ff914000.iommu, rkisp1
+ 45: 0 0 0 0 0 0 GICv3 76 Level ff924000.iommu
+ 46: 0 0 0 0 0 0 GICv3 85 Level ff1d0000.spi
+ 47: 0 0 0 0 0 0 GICv3 91 Level ff110000.i2c
+ 48: 0 0 0 0 0 0 GICv3 66 Level ff130000.i2c
+ 49: 264 0 1386 0 0 0 GICv3 89 Level ff3c0000.i2c
+ 50: 0 0 0 0 0 0 rockchip_gpio_irq 21 Level rk808
+ 56: 0 0 0 0 0 0 rk808 5 Edge RTC alarm
+ 60: 54 0 357 0 0 0 GICv3 88 Level ff3d0000.i2c
+ 61: 0 0 0 0 0 0 GICv3 152 Edge ff848000.watchdog
+ 62: 9491 70670 0 0 0 0 GICv3 97 Level dw-mci
+ 63: 207 0 0 0 0 0 GICv3 43 Level mmc1
+ 64: 0 0 0 0 0 0 rockchip_gpio_irq 7 Edge fe320000.mmc cd
+ 65: 0 0 0 0 0 0 GICv3 129 Level rockchip_thermal
+ 66: 0 0 0 0 0 0 GICv3 94 Level ff100000.saradc
+ 67: 0 0 0 0 0 0 GICv3 58 Level ehci_hcd:usb1
+ 68: 0 0 0 0 0 0 GICv3 62 Level ehci_hcd:usb2
+ 69: 0 0 0 0 0 0 GICv3 60 Level ohci_hcd:usb3
+ 70: 0 0 0 0 0 0 GICv3 64 Level ohci_hcd:usb4
+ 71: 37 0 0 33541 0 0 GICv3 44 Level eth0
+ 72: 0 0 0 0 0 0 GICv3 137 Level xhci-hcd:usb5
+ 73: 52061 0 0 0 0 0 GICv3 142 Level xhci-hcd:usb7
+ 74: 0 0 0 0 0 0 GICv3 87 Level ff680000.rga
+ 75: 0 0 0 0 0 0 GICv3 148 Level ff660000.video-codec
+ 76: 0 0 0 0 0 0 GICv3 146 Level ff650000.video-codec
+ 77: 0 0 0 0 0 0 GICv3 145 Level ff650000.video-codec
+ 78: 0 0 0 0 0 0 GICv3 55 Level ff940000.hdmi
+ 79: 50468710 0 0 0 0 0 rockchip_gpio_irq 16 Level adis16505
+IPI0: 629986 29367897 1604808 9830343 7634766 7528717 Rescheduling interrupts
+IPI1: 11040 79148 22290 41912 29091 21995 Function call interrupts
+IPI2: 0 0 0 0 0 0 CPU stop interrupts
+IPI3: 0 0 0 0 0 0 CPU stop (for crash dump) interrupts
+IPI4: 5391338 7612504 8510753 10479370 10766273 10712987 Timer broadcast interrupts
+IPI5: 3040632 140996 4922363 50728 31487 24500 IRQ work interrupts
+IPI6: 0 0 0 0 0 0 CPU wake-up interrupts
+Err: 0
+)contents";
+
+// Tests that the rock pi's /proc/interrupts is parseable.
+TEST(InterruptsStatusTest, RockPi) {
+ InterruptsStatus status;
+ for (int i = 0; i < 2; ++i) {
+ status.Update(kRockPiContents);
+ EXPECT_EQ(status.states()[0].interrupt_number, 23);
+ EXPECT_EQ(status.states()[0].chip_name, "GICv3");
+ EXPECT_THAT(status.states()[0].count,
+ ::testing::ElementsAre(4221703, 2325251, 5737616, 1332002,
+ 1418781, 1368697));
+ EXPECT_EQ(status.states()[0].hwirq, "30");
+ EXPECT_THAT(status.states()[0].actions,
+ ::testing::ElementsAre("arch_timer"));
+
+ EXPECT_EQ(status.states()[15].interrupt_number, 44);
+ EXPECT_EQ(status.states()[15].chip_name, "GICv3");
+ EXPECT_THAT(status.states()[15].count,
+ ::testing::ElementsAre(0, 0, 3412401, 0, 0, 0));
+ EXPECT_EQ(status.states()[15].hwirq, "75");
+ EXPECT_THAT(status.states()[15].actions,
+ ::testing::ElementsAre("ff914000.iommu", "rkisp1"));
+ }
+
+ status.Update(kRockPiContents2);
+
+ EXPECT_EQ(status.states()[0].interrupt_number, 23);
+ EXPECT_EQ(status.states()[0].chip_name, "GICv3");
+ EXPECT_THAT(status.states()[0].count,
+ ::testing::ElementsAre(1703, 25251, 737616, 2002, 18781, 368697));
+ EXPECT_EQ(status.states()[0].hwirq, "30");
+ EXPECT_THAT(status.states()[0].actions, ::testing::ElementsAre("arch_timer"));
+}
+
+constexpr std::string_view kAustinDesktopContents =
+ R"contents( CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15 CPU16 CPU17 CPU18 CPU19
+ 8: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 8-edge rtc0
+ 9: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 9-fasteoi acpi
+ 14: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 14-fasteoi INTC1056:00
+ 16: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 IR-IO-APIC 16-fasteoi peak_pciefd, peak_pciefd, peak_pciefd, peak_pciefd
+ 17: 0 0 0 0 0 0 0 0 0 1623 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 17-fasteoi snd_hda_intel:card1
+ 18: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 687 0 IR-IO-APIC 18-fasteoi i801_smbus, snd_hda_intel:card2
+ 27: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 27-fasteoi idma64.0, i2c_designware.0
+ 29: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 29-fasteoi idma64.2, i2c_designware.2
+ 40: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-IO-APIC 40-fasteoi idma64.1, i2c_designware.1
+ 120: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DMAR-MSI 0-edge dmar0
+ 121: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DMAR-MSI 1-edge dmar1
+ 136: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 IR-PCI-MSI 229376-edge vmd0
+ 137: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229377-edge vmd0
+ 138: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229378-edge vmd0
+ 139: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229379-edge vmd0
+ 140: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229380-edge vmd0
+ 141: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229381-edge vmd0
+ 142: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229382-edge vmd0
+ 143: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229383-edge vmd0
+ 144: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229384-edge vmd0
+ 145: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229385-edge vmd0
+ 146: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229386-edge vmd0
+ 147: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229387-edge vmd0
+ 148: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229388-edge vmd0
+ 149: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229389-edge vmd0
+ 150: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229390-edge vmd0
+ 151: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229391-edge vmd0
+ 152: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229392-edge vmd0
+ 153: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229393-edge vmd0
+ 154: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 229394-edge vmd0
+ 155: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 124 IR-PCI-MSI 5767168-edge thunderbolt
+ 156: 124 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 5767169-edge thunderbolt
+ 171: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145728-edge enp6s0
+ 172: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145729-edge enp6s0
+ 173: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145730-edge enp6s0
+ 174: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145731-edge enp6s0
+ 175: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145732-edge enp6s0
+ 176: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145733-edge enp6s0
+ 177: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145734-edge enp6s0
+ 178: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145735-edge enp6s0
+ 179: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3145736-edge enp6s0
+ 181: 0 30132468 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 327680-edge xhci_hcd
+ 182: 0 0 0 0 0 251984 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572864-edge nvme0q0
+ 183: 0 0 0 1327707 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 376832-edge ahci[0000:00:17.0]
+ 184: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 33030144-edge xhci_hcd
+ 185: 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572865-edge nvme0q1
+ 186: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572866-edge nvme0q2
+ 187: 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572867-edge nvme0q3
+ 188: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572868-edge nvme0q4
+ 189: 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572869-edge nvme0q5
+ 190: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572870-edge nvme0q6
+ 191: 0 0 0 0 0 0 60 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572871-edge nvme0q7
+ 192: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572872-edge nvme0q8
+ 193: 0 0 0 0 0 0 0 0 43 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572873-edge nvme0q9
+ 194: 0 0 0 0 0 0 0 0 0 34 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572874-edge nvme0q10
+ 195: 0 0 0 0 0 0 0 0 0 0 13 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572875-edge nvme0q11
+ 196: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572876-edge nvme0q12
+ 197: 0 0 0 0 0 0 0 0 0 0 0 0 58 0 0 0 0 0 0 0 IR-PCI-MSI 1572877-edge nvme0q13
+ 198: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572878-edge nvme0q14
+ 199: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 90 0 0 0 0 0 IR-PCI-MSI 1572879-edge nvme0q15
+ 200: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 IR-PCI-MSI 1572880-edge nvme0q16
+ 201: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0 0 0 IR-PCI-MSI 1572881-edge nvme0q17
+ 202: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 35 0 0 IR-PCI-MSI 1572882-edge nvme0q18
+ 203: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 IR-PCI-MSI 1572883-edge nvme0q19
+ 204: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 1572884-edge nvme0q20
+ 205: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 IR-PCI-MSI 4194304-edge enp8s0
+ 206: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 42818 0 IR-PCI-MSI 4194305-edge enp8s0-TxRx-0
+ 207: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 42467 IR-PCI-MSI 4194306-edge enp8s0-TxRx-1
+ 208: 0 0 0 0 0 0 0 0 0 0 0 0 0 42734 0 0 0 0 0 0 IR-PCI-MSI 4194307-edge enp8s0-TxRx-2
+ 209: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 42817 0 0 0 0 0 IR-PCI-MSI 4194308-edge enp8s0-TxRx-3
+ 210: 0 0 0 0 0 0 0 0 352 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 32768-edge i915
+ 211: 0 0 0 0 0 0 0 0 0 0 2545 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 514048-edge snd_hda_intel:card0
+ 212: 0 0 0 0 0 0 0 0 0 0 0 50 0 0 0 0 0 0 0 0 IR-PCI-MSI 360448-edge mei_me
+ 213: 0 0 0 0 0 0 0 0 0 0 0 0 827795 0 0 0 0 0 0 0 IR-PCI-MSI 3670016-edge iwlwifi:default_queue
+ 214: 44562 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670017-edge iwlwifi:queue_1
+ 215: 0 37107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670018-edge iwlwifi:queue_2
+ 216: 0 0 33498 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670019-edge iwlwifi:queue_3
+ 217: 0 0 0 29533 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670020-edge iwlwifi:queue_4
+ 218: 0 0 0 0 21427 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670021-edge iwlwifi:queue_5
+ 219: 0 0 0 0 0 26244 0 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670022-edge iwlwifi:queue_6
+ 220: 0 0 0 0 0 0 22479 0 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670023-edge iwlwifi:queue_7
+ 221: 0 0 0 0 0 0 0 84711 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670024-edge iwlwifi:queue_8
+ 222: 0 0 0 0 0 0 0 0 22071 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670025-edge iwlwifi:queue_9
+ 223: 0 0 0 0 0 0 0 0 0 47348 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670026-edge iwlwifi:queue_10
+ 224: 0 0 0 0 0 0 0 0 0 0 30472 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670027-edge iwlwifi:queue_11
+ 225: 0 0 0 0 0 0 0 0 0 0 0 31430 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670028-edge iwlwifi:queue_12
+ 226: 0 0 0 0 0 0 0 0 0 0 0 0 23299 0 0 0 0 0 0 0 IR-PCI-MSI 3670029-edge iwlwifi:queue_13
+ 227: 0 0 0 0 0 0 0 0 0 0 0 0 0 109672 0 0 0 0 0 0 IR-PCI-MSI 3670030-edge iwlwifi:queue_14
+ 228: 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 IR-PCI-MSI 3670031-edge iwlwifi:exception
+ 229: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4120250 0 0 0 IR-PCI-MSI 524288-edge nvidia
+ 230: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 138205 0 0 0 0 IR-PCI-MSI 1048576-edge nvidia
+ NMI: 92 28 100 10 103 11 107 6 243 6 258 6 157 6 141 6 83 42 24 14 Non-maskable interrupts
+ LOC: 8019100 2732118 7530896 1145194 7715584 1787839 8532279 820360 10887596 601426 12029097 810678 11081674 781984 10455644 576294 5640682 3954630 2196579 1312346 Local timer interrupts
+ SPU: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Spurious interrupts
+ PMI: 92 28 100 10 103 11 107 6 243 6 258 6 157 6 141 6 83 42 24 14 Performance monitoring interrupts
+ IWI: 1 0 0 0 0 0 0 0 24 0 45 0 0 0 0 0 0 0 0 0 IRQ work interrupts
+ RTR: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 APIC ICR read retries
+ RES: 199167 103904 200210 70287 231170 96596 247332 58629 202889 49773 231943 50596 293734 60653 290320 64894 564256 359718 233415 190403 Rescheduling interrupts
+ CAL: 1375045 393024 796832 151717 815698 182423 836372 114075 1150075 122234 1201956 117418 989111 117108 974193 104812 661099 523054 386579 295224 Function call interrupts
+ TLB: 424698 66883 450735 56137 444850 46655 450670 39023 723259 44390 748901 39980 537343 36037 516461 34380 317421 255858 180013 130541 TLB shootdowns
+ TRM: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Thermal event interrupts
+ THR: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Threshold APIC interrupts
+ DFR: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Deferred Error APIC interrupts
+ MCE: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Machine check exceptions
+ MCP: 270 271 271 271 271 271 271 271 271 271 271 271 271 271 271 271 271 271 271 271 Machine check polls
+ ERR: 0
+ MIS: 0
+ PIN: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Posted-interrupt notification event
+ NPI: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Nested posted-interrupt event
+ PIW: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Posted-interrupt wakeup event
+)contents";
+
+// Tests that Austin's desktop's /proc/interrupts is parsable.
+TEST(InterruptsStatusTest, Desktop) {
+ InterruptsStatus status;
+ status.Update(kAustinDesktopContents);
+ status.Update(kAustinDesktopContents);
+ EXPECT_EQ(status.states()[0].interrupt_number, 8);
+ EXPECT_EQ(status.states()[0].chip_name, "IR-IO-APIC");
+ EXPECT_THAT(status.states()[0].count,
+ ::testing::ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ EXPECT_EQ(status.states()[0].hwirq, "8-edge");
+ EXPECT_THAT(status.states()[0].actions, ::testing::ElementsAre("rtc0"));
+}
+
+} // aos::testing