Skip to content

Commit e50d324

Browse files
srivathsa729brenns10
authored andcommitted
Add vectorinfo module
Prints x86_64 per-cpu and system vector information. Orabug: 38383772 Authored-by: Partha Satapathy [email protected] Co-authored-by: Srivathsa Dara [email protected]
1 parent 4ff6c22 commit e50d324

File tree

2 files changed

+249
-0
lines changed

2 files changed

+249
-0
lines changed

drgn_tools/vectorinfo.py

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# Copyright (c) 2025, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
"""
4+
Helper to print x86_64 CPU vector information
5+
"""
6+
import argparse
7+
8+
from drgn import Object
9+
from drgn import Program
10+
from drgn.helpers.linux.percpu import for_each_online_cpu
11+
from drgn.helpers.linux.percpu import per_cpu
12+
from drgn.helpers.linux.percpu import per_cpu_ptr
13+
14+
from drgn_tools.corelens import CorelensModule
15+
from drgn_tools.table import Table
16+
from drgn_tools.util import has_member
17+
18+
19+
"""
20+
Linux IRQ vector layout (x86).
21+
* Vectors 0 ... 31 : system traps and exceptions - hardcoded events (RESERVED)
22+
* Vectors 32 ... 127 : device interrupts
23+
* Vector 128 : legacy int80 syscall interface (RESERVED)
24+
* Vectors 129 ... LOCAL_TIMER_VECTOR-1
25+
* Vectors LOCAL_TIMER_VECTOR ... 255 : special interrupts (RESERVED)
26+
27+
Reserved vectors = 32 (system traps and exceptions) + 1 (sycall interface)
28+
+ 20 (special interrupts)
29+
"""
30+
31+
NR_VECTORS = 256
32+
IA32_SYSCALL_VECTOR = 0x80
33+
FIRST_EXTERNAL_VECTOR = 0x20
34+
LOCAL_TIMER_VECTOR = 0xEC
35+
RESERVED_VECTORS = 53
36+
37+
38+
def dump_vectors(vectors: str) -> None:
39+
"""
40+
Print the given string with a space after every 8 characters,
41+
and a newline after every 64 characters.
42+
"""
43+
for i in range(0, len(vectors), 8):
44+
if i and i % 64 == 0:
45+
print()
46+
print(vectors[i : i + 8], end=" ")
47+
print()
48+
49+
50+
def print_vector_matrix(prog: Program) -> None:
51+
"""
52+
Print vector matrix information
53+
"""
54+
try:
55+
vector_matrix = prog["vector_matrix"]
56+
except KeyError:
57+
print("\n'vector_matrix' not found — requires UEK6 or newer.\n")
58+
return
59+
per_cpu_vector_map = vector_matrix.maps
60+
print("System vector matrix:")
61+
print(f"\t{'Vector bits':25}: {vector_matrix.matrix_bits.value_()}")
62+
print(
63+
f"\t{'Vector allocation start':25}: {vector_matrix.alloc_start.value_()}"
64+
)
65+
print(
66+
f"\t{'Vector allocation end':25}: {vector_matrix.alloc_end.value_()}"
67+
)
68+
print(
69+
f"\t{'Vector allocation size':25}: {vector_matrix.alloc_size.value_()}"
70+
)
71+
print(
72+
f"\t{'Global available':25}: {vector_matrix.global_available.value_()}"
73+
)
74+
print(
75+
f"\t{'Global reserved':25}: {vector_matrix.global_reserved.value_()}"
76+
)
77+
print(
78+
f"\t{'Total allocated':25}: {vector_matrix.total_allocated.value_()}"
79+
)
80+
print(f"\t{'Online maps':25}: {vector_matrix.online_maps.value_()}")
81+
print()
82+
83+
for cpu in for_each_online_cpu(prog):
84+
per_cpu_vector_matrix = per_cpu_ptr(per_cpu_vector_map, cpu)
85+
print(f"Per-CPU IRQ vector map (CPU {cpu}):")
86+
print(
87+
f"\t{'Available':25}: {per_cpu_vector_matrix.available.value_()}"
88+
)
89+
print(
90+
f"\t{'Allocated':25}: {per_cpu_vector_matrix.allocated.value_()}"
91+
)
92+
print(f"\t{'Managed':25}: {per_cpu_vector_matrix.managed.value_()}")
93+
print(
94+
f"\t{'Managed Allocated':25}: {per_cpu_vector_matrix.managed_allocated.value_()}"
95+
)
96+
print()
97+
98+
99+
def print_vectors(prog: Program, verbose: bool) -> None:
100+
"""
101+
Print vector information
102+
103+
If -v flag specified, print per vector information
104+
"""
105+
VECTOR_SHUTDOWN = Object(prog, "void *", -1)
106+
VECTOR_RETRIGGERED = Object(prog, "void *", -2)
107+
vector_irq = prog["vector_irq"]
108+
total_vectors_used = 0
109+
total_cpus = 0
110+
total_avl_vectors = 0
111+
system_vectors = []
112+
system_table = Table(
113+
[
114+
"CPU",
115+
"Total Vectors",
116+
"Used Vectors",
117+
"Reserved Vectors",
118+
"Available Vectors",
119+
]
120+
)
121+
for cpu in for_each_online_cpu(prog):
122+
total_cpus += 1
123+
cpu_vector_list = ["0"] * NR_VECTORS
124+
cpu_vectors = per_cpu(vector_irq, cpu)
125+
per_cpu_vectors_used = 0
126+
per_cpu_avl_vectors = 0
127+
128+
if verbose:
129+
print(f"\nCPU : {cpu}")
130+
per_cpu_table = Table(
131+
["Vector", "IRQ", "Address", "Function", "Device", "Module"]
132+
)
133+
134+
for vec in range(NR_VECTORS):
135+
if not cpu_vectors[vec]:
136+
if (
137+
vec >= FIRST_EXTERNAL_VECTOR
138+
and vec < LOCAL_TIMER_VECTOR
139+
and vec != IA32_SYSCALL_VECTOR
140+
):
141+
per_cpu_avl_vectors += 1
142+
continue
143+
irqdesc = cpu_vectors[vec]
144+
per_cpu_vectors_used += 1
145+
cpu_vector_list[vec] = "1"
146+
147+
if not verbose:
148+
continue
149+
150+
irqdesc_irq = irqdesc_fn = irqdesc_dev = irqdesc_mod = ""
151+
if irqdesc == VECTOR_SHUTDOWN:
152+
irqdesc_addr = "SHUTDOWN"
153+
elif irqdesc == VECTOR_RETRIGGERED:
154+
irqdesc_addr = "RETRIGGERED"
155+
else:
156+
irqdesc_addr = hex(irqdesc)
157+
irqdesc_irq = irqdesc_fn = irqdesc_dev = irqdesc_mod = "NA"
158+
if irqdesc.action:
159+
irqdesc_irq = irqdesc.action.irq.value_()
160+
if irqdesc.action.name:
161+
irqdesc_fn = irqdesc.action.name.string_().decode(
162+
"utf-8"
163+
)
164+
165+
if has_member(irqdesc, "dev_name") and irqdesc.dev_name:
166+
irqdesc_dev = irqdesc.dev_name.string_().decode("utf-8")
167+
168+
if irqdesc.owner and irqdesc.owner.name:
169+
irqdesc_mod = irqdesc.owner.name.string_().decode("utf-8")
170+
171+
per_cpu_table.row(
172+
vec,
173+
irqdesc_irq,
174+
irqdesc_addr,
175+
irqdesc_fn,
176+
irqdesc_dev,
177+
irqdesc_mod,
178+
)
179+
180+
if verbose:
181+
per_cpu_table.write()
182+
print(
183+
f"Total: {NR_VECTORS}\tUsed: {per_cpu_vectors_used}\tReserved: {RESERVED_VECTORS}\tAvailable: {per_cpu_avl_vectors}"
184+
)
185+
186+
system_vectors.append("".join(cpu_vector_list))
187+
188+
total_vectors_used += per_cpu_vectors_used
189+
total_avl_vectors += per_cpu_avl_vectors
190+
system_table.row(
191+
cpu,
192+
NR_VECTORS,
193+
per_cpu_vectors_used,
194+
RESERVED_VECTORS,
195+
per_cpu_avl_vectors,
196+
)
197+
198+
total_system_vectors = NR_VECTORS * total_cpus
199+
total_res_vectors = RESERVED_VECTORS * total_cpus
200+
for i in range(total_cpus):
201+
print(f"\nCPU : {i}")
202+
dump_vectors(system_vectors[i])
203+
print()
204+
system_table.row(
205+
"Total",
206+
total_system_vectors,
207+
total_vectors_used,
208+
total_res_vectors,
209+
total_avl_vectors,
210+
)
211+
system_table.write()
212+
213+
214+
def print_vector_info(prog: Program, verbose: bool = False) -> None:
215+
"""
216+
Helper to print CPU vector information
217+
"""
218+
if prog.platform.arch.name != "X86_64":
219+
print("Non-x86_64 architectures are not supported at this time")
220+
return
221+
print_vector_matrix(prog)
222+
print_vectors(prog, verbose)
223+
224+
225+
class VectorInfo(CorelensModule):
226+
"""
227+
Prints x86_64 interrupt vector information
228+
"""
229+
230+
name = "vectorinfo"
231+
232+
def add_args(self, parser: argparse.ArgumentParser) -> None:
233+
parser.add_argument(
234+
"-v",
235+
"--verbose",
236+
action="store_true",
237+
help="Print per vector information",
238+
)
239+
240+
def run(self, prog: Program, args: argparse.Namespace) -> None:
241+
print_vector_info(prog, verbose=args.verbose)

tests/test_vectorinfo.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
from drgn_tools import vectorinfo
4+
5+
6+
def test_vectorinfo(prog):
7+
vectorinfo.print_vector_matrix(prog)
8+
vectorinfo.print_vectors(prog, True)

0 commit comments

Comments
 (0)