File: stap-hw-breakpoint.h

package info (click to toggle)
systemtap 5.1-5
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 47,964 kB
  • sloc: cpp: 80,838; ansic: 54,757; xml: 49,725; exp: 43,665; sh: 11,527; python: 5,003; perl: 2,252; tcl: 1,312; makefile: 1,006; javascript: 149; lisp: 105; awk: 101; asm: 91; java: 70; sed: 16
file content (162 lines) | stat: -rw-r--r-- 4,589 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* -*- linux-c -*-
 * Utility functions for handling haredware breakpoints (watchpoints).
 *
 * Copyright (C) 2023 by OpenResty Inc.
 * This file is part of systemtap, and is free software.  You can
 * redistribute it and/or modify it under the terms of the GNU General
 * Public License (GPL); either version 2, or (at your option) any
 * later version.
 */

#ifndef _STP_HW_BREAKPOINT_H_INCLUDED_
#define _STP_HW_BREAKPOINT_H_INCLUDED_

struct stap_hwbkpt_probe {
	bool kernel_p;

	bool registered_p;
	// registered_p = false signifies a probe that is unregistered (or failed)
	// registered_p = true signifies a probe that got registered successfully

	uint8_t atype;
	unsigned int len;

	// Symbol Names are mostly small and uniform enough
	// to justify putting const char*.
	const char * const symbol;

	const unsigned long address;
	const struct stap_probe * const probe;
};

static inline int
stap_hwbkpt_init(perf_overflow_handler_t triggered, struct stap_hwbkpt_probe *probes, int probe_count,
	struct perf_event_attr *probe_array, void *ret_array[], const char **probe_point_ptr)
{
	int rc = 0;
	int i;

	if (unlikely(probe_count == 0))  /* do nothing */
		return 0;

	for (i=0; i<probe_count; i++) {
		struct stap_hwbkpt_probe *skp = & probes[i];
		struct perf_event_attr *hp = & probe_array[i];
		void *addr = (void *) skp->address;
		const char *hwbkpt_symbol_name = addr ? NULL : skp->symbol;
		hw_breakpoint_init(hp);
		if (addr)
			hp->bp_addr = (unsigned long) addr;
		else {
			hp->bp_addr = kallsyms_lookup_name(hwbkpt_symbol_name);
			if (!hp->bp_addr) {
				_stp_error("Probe %s registration failed: invalid symbol '%s' ",
					   skp->probe->pp, hwbkpt_symbol_name);
				rc = -EINVAL;
				skp->registered_p = false;
				break;
			}
		}
		hp->bp_type = skp->atype;

		// Convert actual len to bp len.
		switch(skp->len) {
			case 1:
				hp->bp_len = HW_BREAKPOINT_LEN_1;
				break;
			case 2:
				hp->bp_len = HW_BREAKPOINT_LEN_2;
				break;
			case 3:
			case 4:
				hp->bp_len = HW_BREAKPOINT_LEN_4;
				break;
			case 5:
			case 6:
			case 7:
			case 8:
			default: // XXX: could instead reject
				hp->bp_len = HW_BREAKPOINT_LEN_8;
				break;
		}

		*probe_point_ptr = skp->probe->pp; // for error messages

#ifdef STAPCONF_HW_BREAKPOINT_CONTEXT
		if (skp->kernel_p) {
#ifdef DEBUG_PROBES
			pr_warn("%s:%d: registering kernel-mode hw breakpoint at %#lx\n",
				__func__, __LINE__, (unsigned long) hp->bp_addr);
#endif
			ret_array[i] = register_wide_hw_breakpoint(hp, triggered, NULL);
		} else {
			if (likely(_stp_target > 0)) {
				struct task_struct *tsk;
				rcu_read_lock();
				tsk = get_pid_task(find_vpid(_stp_orig_target), PIDTYPE_PID);
				rcu_read_unlock();
				if (unlikely(tsk == NULL)) {
					ret_array[i] = ERR_PTR(-ESRCH);
				} else {
#ifdef DEBUG_PROBES
					pr_warn("%s:%d: registering user hw breakpoint for pid %d (%s) at addr %#lx\n",
						__func__, __LINE__, _stp_target, tsk->comm,
						(unsigned long) hp->bp_addr);
#endif
					ret_array[i] = register_user_hw_breakpoint(hp, triggered, NULL, tsk);
					put_task_struct(tsk);
				}  /* tsk != NULL */
			} else {  /* _stp_target <= 0 */
				ret_array[i] = ERR_PTR(-ESRCH);
			}
		}  /* !skp->kernel_p */
#else  /* !defined(STAPCONF_HW_BREAKPOINT_CONTEXT) */
		if (unlikely(!skp->kernel_p))
			ret_array[i] = ERR_PTR(-ENOTSUP);
		else
			ret_array[i] = register_wide_hw_breakpoint(hp, triggered);
#endif
		if (unlikely(IS_ERR(ret_array[i]))) {
			rc = PTR_ERR(ret_array[i]);
			ret_array[i] = 0;
			_stp_error("Hwbkpt probe %s: registration error [man warning::pass5] %d, addr %#lx, name %s",
				   skp->probe->pp, rc, (unsigned long) addr, hwbkpt_symbol_name);
			skp->registered_p = false;
			break;
		}
		else skp->registered_p = true;
	} // for loop

	if (unlikely(rc)) {
		if (unlikely(i >= probe_count)) {
			i = probe_count - 1;
		}
		for (; i >= 0; i--) {
			struct stap_hwbkpt_probe *skp = & probes[i];
			if (unlikely(!skp->registered_p)) continue;
			unregister_wide_hw_breakpoint(ret_array[i]);
			skp->registered_p = false;
		}
	}

	return rc;
}

static inline void
stap_hwbkpt_exit(struct stap_hwbkpt_probe *probes, int probe_count, void *ret_array[])
{
	int i;
	//Unregister hwbkpt probes.
	for (i=0; i<probe_count; i++) {
		struct stap_hwbkpt_probe *skp = & probes[i];
		if (unlikely(!skp->registered_p)) continue;
		if (skp->kernel_p) {
			unregister_wide_hw_breakpoint((struct perf_event **) ret_array[i]);
		} else {
			unregister_hw_breakpoint((struct perf_event *) ret_array[i]);
		}
		skp->registered_p = false;
	}
}

#endif  /* _STP_HW_BREAKPOINT_H_INCLUDED_ */