1/*
2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "CliPrintVariableCommand.h"
8
9#include <stdio.h>
10
11#include <AutoLocker.h>
12
13#include "CliContext.h"
14#include "StackFrame.h"
15#include "StackTrace.h"
16#include "Team.h"
17#include "Type.h"
18#include "UiUtils.h"
19#include "UserInterface.h"
20#include "ValueLocation.h"
21#include "ValueNode.h"
22#include "ValueNodeContainer.h"
23#include "ValueNodeManager.h"
24
25
26CliPrintVariableCommand::CliPrintVariableCommand()
27	:
28	CliCommand("print value(s) of a variable",
29		"%s [--depth n] variable [variable2 ...]\n"
30		"Prints the value and members of the named variable.")
31{
32}
33
34
35void
36CliPrintVariableCommand::Execute(int argc, const char* const* argv,
37	CliContext& context)
38{
39	if (argc < 2) {
40		PrintUsage(argv[0]);
41		return;
42	}
43
44	ValueNodeManager* manager = context.GetValueNodeManager();
45
46	ValueNodeContainer* container = manager->GetContainer();
47	AutoLocker<ValueNodeContainer> containerLocker(container);
48	if (container == NULL || container->CountChildren() == 0) {
49		printf("No variables available.\n");
50		return;
51	}
52
53	int32 depth = 1;
54	int32 i = 1;
55	for (; i < argc; i++) {
56		if (strcmp(argv[i], "--depth") == 0) {
57			if (i == argc - 1) {
58				printf("Error: An argument must be supplied for depth.\n");
59				return;
60			}
61			char* endPointer;
62			depth = strtol(argv[i + 1], &endPointer, 0);
63			if (*endPointer != '\0' || depth < 0) {
64				printf("Error: Invalid parameter \"%s\"\n", argv[i + 1]);
65				return;
66			}
67			i++;
68		}
69		else
70			break;
71	}
72
73	if (i == argc) {
74		printf("Error: At least one variable name must be supplied.\n");
75		return;
76	}
77
78	bool found = false;
79	while (i < argc) {
80		// TODO: support variable expressions in addition to just names.
81		const char* variableName = argv[i++];
82		for (int32 j = 0; ValueNodeChild* child = container->ChildAt(j); j++) {
83			if (child->Name() == variableName) {
84				found = true;
85				containerLocker.Unlock();
86				_ResolveValueIfNeeded(child->Node(), context, depth);
87				containerLocker.Lock();
88				BString data;
89				UiUtils::PrintValueNodeGraph(data, child, 1, depth);
90				printf("%s", data.String());
91			}
92		}
93
94		if (!found)
95			printf("No such variable: %s\n", variableName);
96		found = false;
97	}
98}
99
100
101status_t
102CliPrintVariableCommand::_ResolveValueIfNeeded(ValueNode* node,
103	CliContext& context, int32 maxDepth)
104{
105	StackFrame* frame = context.GetStackTrace()->FrameAt(
106		context.CurrentStackFrameIndex());
107	if (frame == NULL)
108		return B_BAD_DATA;
109
110	status_t result = B_OK;
111	ValueNodeManager* manager = context.GetValueNodeManager();
112	ValueNodeContainer* container = manager->GetContainer();
113	AutoLocker<ValueNodeContainer> containerLocker(container);
114	if (node->LocationAndValueResolutionState() == VALUE_NODE_UNRESOLVED) {
115		context.GetUserInterfaceListener()->ValueNodeValueRequested(
116			context.CurrentThread()->GetCpuState(), container, node);
117
118
119		while (node->LocationAndValueResolutionState()
120			== VALUE_NODE_UNRESOLVED) {
121			containerLocker.Unlock();
122			context.WaitForEvent(CliContext::MSG_VALUE_NODE_CHANGED);
123			containerLocker.Lock();
124			if (context.IsTerminating())
125				return B_ERROR;
126		}
127	}
128
129	if (node->LocationAndValueResolutionState() == B_OK && maxDepth > 0) {
130		for (int32 i = 0; i < node->CountChildren(); i++) {
131			ValueNodeChild* child = node->ChildAt(i);
132			containerLocker.Unlock();
133			result = manager->AddChildNodes(child);
134			if (result != B_OK)
135				continue;
136
137			// since in the case of a pointer to a compound we hide
138			// the intervening compound, don't consider the hidden node
139			// a level for the purposes of depth traversal
140			if (node->GetType()->Kind() == TYPE_ADDRESS
141				&& child->GetType()->Kind() == TYPE_COMPOUND) {
142				_ResolveValueIfNeeded(child->Node(), context, maxDepth);
143			} else
144				_ResolveValueIfNeeded(child->Node(), context, maxDepth - 1);
145			containerLocker.Lock();
146		}
147	}
148
149	return result;
150}
151