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