xref: /haiku/src/system/kernel/debug/debug_variables.cpp (revision 3be9edf8da228afd9fec0390f408c964766122aa)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "debug_variables.h"
8 
9 #include <string.h>
10 
11 #include <KernelExport.h>
12 
13 #include <arch/debug.h>
14 #include <debug.h>
15 #include <elf.h>
16 #include <util/DoublyLinkedList.h>
17 
18 
19 static const int kVariableCount				= 64;
20 static const int kTemporaryVariableCount	= 32;
21 static const char kTemporaryVariablePrefix	= '_';
22 static const char kArchSpecificVariablePrefix = '$';
23 static const char kSymbolVariablePrefix = '@';
24 static const char* const kCommandReturnValueVariable = "_";
25 
26 
27 struct Variable {
28 	char	name[MAX_DEBUG_VARIABLE_NAME_LEN];
29 	uint64	value;
30 
31 	inline bool IsUsed() const
32 	{
33 		return name[0] != '\0';
34 	}
35 
36 	void Init(const char* variableName)
37 	{
38 		strlcpy(name, variableName, sizeof(name));
39 	}
40 
41 	void Uninit()
42 	{
43 		name[0] = '\0';
44 	}
45 
46 	inline bool HasName(const char* variableName) const
47 	{
48 		return strncmp(name, variableName, sizeof(name)) == 0;
49 	}
50 };
51 
52 struct TemporaryVariable : Variable,
53 		DoublyLinkedListLinkImpl<TemporaryVariable> {
54 };
55 
56 static Variable sVariables[kVariableCount];
57 static TemporaryVariable sTemporaryVariables[kTemporaryVariableCount];
58 
59 static DoublyLinkedList<TemporaryVariable> sTemporaryVariablesLRUQueue;
60 
61 
62 static inline bool
63 is_temporary_variable(const char* variableName)
64 {
65 	return variableName[0] == kTemporaryVariablePrefix;
66 }
67 
68 
69 static inline bool
70 is_arch_specific_variable(const char* variableName)
71 {
72 	return variableName[0] == kArchSpecificVariablePrefix;
73 }
74 
75 
76 static inline bool
77 is_symbol_variable(const char* variableName)
78 {
79 	return variableName[0] == kSymbolVariablePrefix;
80 }
81 
82 
83 static void
84 dequeue_temporary_variable(TemporaryVariable* variable)
85 {
86 	// dequeue if queued
87 	if (variable->GetDoublyLinkedListLink()->previous != NULL
88 		|| sTemporaryVariablesLRUQueue.Head() == variable) {
89 		sTemporaryVariablesLRUQueue.Remove(variable);
90 	}
91 }
92 
93 
94 static void
95 unset_variable(Variable* variable)
96 {
97 	if (is_temporary_variable(variable->name))
98 		dequeue_temporary_variable(static_cast<TemporaryVariable*>(variable));
99 
100 	variable->Uninit();
101 }
102 
103 
104 static void
105 touch_variable(Variable* _variable)
106 {
107 	if (!is_temporary_variable(_variable->name))
108 		return;
109 
110 	TemporaryVariable* variable = static_cast<TemporaryVariable*>(_variable);
111 
112 	// move to the end of the queue
113 	dequeue_temporary_variable(variable);
114 	sTemporaryVariablesLRUQueue.Add(variable);
115 }
116 
117 
118 static Variable*
119 free_temporary_variable_slot()
120 {
121 	TemporaryVariable* variable = sTemporaryVariablesLRUQueue.RemoveHead();
122 	if (variable)
123 		variable->Uninit();
124 
125 	return variable;
126 }
127 
128 
129 static Variable*
130 get_variable(const char* variableName, bool create)
131 {
132 	// find the variable in the respective array and a free slot, we can
133 	// use, if it doesn't exist yet
134 	Variable* freeSlot = NULL;
135 
136 	if (is_temporary_variable(variableName)) {
137 		// temporary variable
138 		for (int i = 0; i < kTemporaryVariableCount; i++) {
139 			TemporaryVariable* variable = sTemporaryVariables + i;
140 
141 			if (!variable->IsUsed()) {
142 				if (freeSlot == NULL)
143 					freeSlot = variable;
144 			} else if (variable->HasName(variableName))
145 				return variable;
146 		}
147 
148 		if (create && freeSlot == NULL)
149 			freeSlot = free_temporary_variable_slot();
150 	} else {
151 		// persistent variable
152 		for (int i = 0; i < kVariableCount; i++) {
153 			Variable* variable = sVariables + i;
154 
155 			if (!variable->IsUsed()) {
156 				if (freeSlot == NULL)
157 					freeSlot = variable;
158 			} else if (variable->HasName(variableName))
159 				return variable;
160 		}
161 	}
162 
163 
164 	if (create && freeSlot != NULL) {
165 		freeSlot->Init(variableName);
166 		return freeSlot;
167 	}
168 
169 	return NULL;
170 }
171 
172 
173 // #pragma mark - debugger commands
174 
175 
176 static int
177 cmd_unset_variable(int argc, char **argv)
178 {
179 	static const char* usage = "usage: unset <variable>\n"
180 		"Unsets the given variable, if it exists.\n";
181 	if (argc != 2 || strcmp(argv[1], "--help") == 0) {
182 		kprintf(usage);
183 		return 0;
184 	}
185 
186 	const char* variable = argv[1];
187 
188 	if (!unset_debug_variable(variable))
189 		kprintf("Did not find variable %s.\n", variable);
190 
191 	return 0;
192 }
193 
194 
195 static int
196 cmd_unset_all_variables(int argc, char **argv)
197 {
198 	static const char* usage = "usage: %s\n"
199 		"Unsets all variables.\n";
200 	if (argc == 2 && strcmp(argv[1], "--help") == 0) {
201 		kprintf(usage, argv[0]);
202 		return 0;
203 	}
204 
205 	unset_all_debug_variables();
206 
207 	return 0;
208 }
209 
210 
211 static int
212 cmd_variables(int argc, char **argv)
213 {
214 	static const char* usage = "usage: vars\n"
215 		"Unsets the given variable, if it exists.\n";
216 	if (argc != 1) {
217 		kprintf(usage);
218 		return 0;
219 	}
220 
221 	// persistent variables
222 	for (int i = 0; i < kVariableCount; i++) {
223 		Variable& variable = sVariables[i];
224 		if (variable.IsUsed()) {
225 			kprintf("%16s: %llu (0x%llx)\n", variable.name, variable.value,
226 				variable.value);
227 		}
228 	}
229 
230 	// temporary variables
231 	for (int i = 0; i < kTemporaryVariableCount; i++) {
232 		Variable& variable = sTemporaryVariables[i];
233 		if (variable.IsUsed()) {
234 			kprintf("%16s: %llu (0x%llx)\n", variable.name, variable.value,
235 				variable.value);
236 		}
237 	}
238 
239 	return 0;
240 }
241 
242 
243 // #pragma mark - kernel public functions
244 
245 
246 bool
247 is_debug_variable_defined(const char* variableName)
248 {
249 	if (get_variable(variableName, false) != NULL)
250 		return true;
251 
252 	if (is_symbol_variable(variableName))
253 		return elf_debug_lookup_symbol(variableName + 1) != 0;
254 
255 	return is_arch_specific_variable(variableName)
256 		&& arch_is_debug_variable_defined(variableName + 1);
257 }
258 
259 
260 bool
261 set_debug_variable(const char* variableName, uint64 value)
262 {
263 	if (is_arch_specific_variable(variableName))
264 		return arch_set_debug_variable(variableName + 1, value) == B_OK;
265 
266 	if (Variable* variable = get_variable(variableName, true)) {
267 		variable->value = value;
268 		touch_variable(variable);
269 		return true;
270 	}
271 
272 	return false;
273 }
274 
275 
276 uint64
277 get_debug_variable(const char* variableName, uint64 defaultValue)
278 {
279 	if (Variable* variable = get_variable(variableName, false)) {
280 		touch_variable(variable);
281 		return variable->value;
282 	}
283 
284 	uint64 value;
285 	if (is_arch_specific_variable(variableName)
286 		&& arch_get_debug_variable(variableName + 1, &value) == B_OK) {
287 		return value;
288 	}
289 
290 	if (is_symbol_variable(variableName)) {
291 		addr_t value = elf_debug_lookup_symbol(variableName + 1);
292 		if (value != 0)
293 			return value;
294 	}
295 
296 	return defaultValue;
297 }
298 
299 
300 bool
301 unset_debug_variable(const char* variableName)
302 {
303 	if (Variable* variable = get_variable(variableName, false)) {
304 		unset_variable(variable);
305 		return true;
306 	}
307 
308 	return false;
309 }
310 
311 
312 void
313 unset_all_debug_variables()
314 {
315 	// persistent variables
316 	for (int i = 0; i < kVariableCount; i++) {
317 		Variable& variable = sVariables[i];
318 		if (variable.IsUsed())
319 			unset_variable(&variable);
320 	}
321 
322 	// temporary variables
323 	for (int i = 0; i < kTemporaryVariableCount; i++) {
324 		Variable& variable = sTemporaryVariables[i];
325 		if (variable.IsUsed())
326 			unset_variable(&variable);
327 	}
328 }
329 
330 
331 void
332 debug_variables_init()
333 {
334 	add_debugger_command("unset", &cmd_unset_variable,
335 		"Unsets the given variable");
336 	add_debugger_command("unset_all", &cmd_unset_all_variables,
337 		"Unsets all variables");
338 	add_debugger_command("vars", &cmd_variables,
339 		"Lists all defined variables with their values");
340 }
341