1/*
2 * Copyright 2012-2015, Rene Gollent, rene@gollent.com.
3 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "Jobs.h"
8
9#include <AutoLocker.h>
10
11#include "Architecture.h"
12#include "CpuState.h"
13#include "DebuggerInterface.h"
14#include "TeamTypeInformation.h"
15#include "Tracing.h"
16#include "Value.h"
17#include "ValueLoader.h"
18#include "ValueLocation.h"
19#include "ValueNode.h"
20#include "ValueNodeContainer.h"
21#include "Variable.h"
22#include "VariableValueNodeChild.h"
23
24
25ResolveValueNodeValueJob::ResolveValueNodeValueJob(
26	DebuggerInterface* debuggerInterface, Architecture* architecture,
27	CpuState* cpuState, TeamTypeInformation* typeInformation,
28	ValueNodeContainer* container, ValueNode* valueNode)
29	:
30	fKey(valueNode, JOB_TYPE_RESOLVE_VALUE_NODE_VALUE),
31	fDebuggerInterface(debuggerInterface),
32	fArchitecture(architecture),
33	fCpuState(cpuState),
34	fTypeInformation(typeInformation),
35	fContainer(container),
36	fValueNode(valueNode)
37{
38	if (fCpuState != NULL)
39		fCpuState->AcquireReference();
40	fContainer->AcquireReference();
41	fValueNode->AcquireReference();
42}
43
44
45ResolveValueNodeValueJob::~ResolveValueNodeValueJob()
46{
47	if (fCpuState != NULL)
48		fCpuState->ReleaseReference();
49	fContainer->ReleaseReference();
50	fValueNode->ReleaseReference();
51}
52
53
54const JobKey&
55ResolveValueNodeValueJob::Key() const
56{
57	return fKey;
58}
59
60
61status_t
62ResolveValueNodeValueJob::Do()
63{
64	// check whether the node still belongs to the container
65	AutoLocker<ValueNodeContainer> containerLocker(fContainer);
66	if (fValueNode->Container() != fContainer)
67		return B_BAD_VALUE;
68
69	// if already resolved, we're done
70	status_t nodeResolutionState
71		= fValueNode->LocationAndValueResolutionState();
72	if (nodeResolutionState != VALUE_NODE_UNRESOLVED)
73		return nodeResolutionState;
74
75	containerLocker.Unlock();
76
77	// resolve
78	status_t error = _ResolveNodeValue();
79	if (error != B_OK) {
80		nodeResolutionState = fValueNode->LocationAndValueResolutionState();
81		if (nodeResolutionState != VALUE_NODE_UNRESOLVED)
82			return nodeResolutionState;
83
84		containerLocker.Lock();
85		fValueNode->SetLocationAndValue(NULL, NULL, error);
86		containerLocker.Unlock();
87	}
88
89	return error;
90}
91
92
93status_t
94ResolveValueNodeValueJob::_ResolveNodeValue()
95{
96	// get the node child and parent node
97	AutoLocker<ValueNodeContainer> containerLocker(fContainer);
98	ValueNodeChild* nodeChild = fValueNode->NodeChild();
99	BReference<ValueNodeChild> nodeChildReference(nodeChild);
100
101	ValueNode* parentNode = nodeChild->Parent();
102	BReference<ValueNode> parentNodeReference(parentNode);
103
104	// Check whether the node child location has been resolved already
105	// (successfully).
106	status_t nodeChildResolutionState = nodeChild->LocationResolutionState();
107	bool nodeChildDone = nodeChildResolutionState != VALUE_NODE_UNRESOLVED;
108	if (nodeChildDone && nodeChildResolutionState != B_OK)
109		return nodeChildResolutionState;
110
111	// If the child node location has not been resolved yet, check whether the
112	// parent node location and value have been resolved already (successfully).
113	bool parentDone = true;
114	if (!nodeChildDone && parentNode != NULL) {
115		status_t parentResolutionState
116			= parentNode->LocationAndValueResolutionState();
117		parentDone = parentResolutionState != VALUE_NODE_UNRESOLVED;
118		if (parentDone && parentResolutionState != B_OK)
119			return parentResolutionState;
120	}
121
122	containerLocker.Unlock();
123
124	// resolve the parent node location and value, if necessary
125	if (!parentDone) {
126		status_t error = _ResolveParentNodeValue(parentNode);
127		if (error != B_OK) {
128			TRACE_LOCALS("ResolveValueNodeValueJob::_ResolveNodeValue(): value "
129				"node: %p (\"%s\"): _ResolveParentNodeValue(%p) failed\n",
130				fValueNode, fValueNode->Name().String(), parentNode);
131			return error;
132		}
133
134		if (State() == JOB_STATE_WAITING)
135			return B_OK;
136	}
137
138	// resolve the node child location, if necessary
139	if (!nodeChildDone) {
140		status_t error = _ResolveNodeChildLocation(nodeChild);
141		if (error != B_OK) {
142			TRACE_LOCALS("ResolveValueNodeValueJob::_ResolveNodeValue(): value "
143				"node: %p (\"%s\"): _ResolveNodeChildLocation(%p) failed\n",
144				fValueNode, fValueNode->Name().String(), nodeChild);
145			return error;
146		}
147	}
148
149	CpuState* variableCpuState = NULL;
150	VariableValueNodeChild* variableChild = dynamic_cast<
151		VariableValueNodeChild*>(nodeChild);
152	if (variableChild != NULL)
153		variableCpuState = variableChild->GetVariable()->GetCpuState();
154
155	// resolve the node location and value
156	ValueLoader valueLoader(fArchitecture, fDebuggerInterface,
157		variableCpuState != NULL ? variableCpuState : fCpuState);
158	ValueLocation* location;
159	Value* value;
160	status_t error = fValueNode->ResolvedLocationAndValue(&valueLoader,
161		location, value);
162	if (error != B_OK) {
163		TRACE_LOCALS("ResolveValueNodeValueJob::_ResolveNodeValue(): value "
164			"node: %p (\"%s\"): fValueNode->ResolvedLocationAndValue() "
165			"failed\n", fValueNode, fValueNode->Name().String());
166		return error;
167	}
168	BReference<ValueLocation> locationReference(location, true);
169	BReference<Value> valueReference(value, true);
170
171	// set location and value on the node
172	containerLocker.Lock();
173	status_t nodeResolutionState
174		= fValueNode->LocationAndValueResolutionState();
175	if (nodeResolutionState != VALUE_NODE_UNRESOLVED)
176		return nodeResolutionState;
177	fValueNode->SetLocationAndValue(location, value, B_OK);
178	containerLocker.Unlock();
179
180	return B_OK;
181}
182
183
184status_t
185ResolveValueNodeValueJob::_ResolveNodeChildLocation(ValueNodeChild* nodeChild)
186{
187	// resolve the location
188	ValueLoader valueLoader(fArchitecture, fDebuggerInterface, fCpuState);
189	ValueLocation* location = NULL;
190	status_t error = nodeChild->ResolveLocation(&valueLoader, location);
191	BReference<ValueLocation> locationReference(location, true);
192
193	// set the location on the node child
194	AutoLocker<ValueNodeContainer> containerLocker(fContainer);
195	status_t nodeChildResolutionState = nodeChild->LocationResolutionState();
196	if (nodeChildResolutionState == VALUE_NODE_UNRESOLVED)
197		nodeChild->SetLocation(location, error);
198	else
199		error = nodeChildResolutionState;
200
201	return error;
202}
203
204
205status_t
206ResolveValueNodeValueJob::_ResolveParentNodeValue(ValueNode* parentNode)
207{
208	AutoLocker<ValueNodeContainer> containerLocker(fContainer);
209
210	if (parentNode->Container() != fContainer)
211		return B_BAD_VALUE;
212
213	// if the parent node already has a value, we're done
214	status_t nodeResolutionState
215		= parentNode->LocationAndValueResolutionState();
216	if (nodeResolutionState != VALUE_NODE_UNRESOLVED)
217		return nodeResolutionState;
218
219	// check whether a job is already in progress
220	AutoLocker<Worker> workerLocker(GetWorker());
221	SimpleJobKey jobKey(parentNode, JOB_TYPE_RESOLVE_VALUE_NODE_VALUE);
222	if (GetWorker()->GetJob(jobKey) == NULL) {
223		workerLocker.Unlock();
224
225		// schedule the job
226		status_t error = GetWorker()->ScheduleJob(
227			new(std::nothrow) ResolveValueNodeValueJob(fDebuggerInterface,
228				fArchitecture, fCpuState, fTypeInformation, fContainer,
229				parentNode));
230		if (error != B_OK) {
231			// scheduling failed -- set the value to invalid
232			parentNode->SetLocationAndValue(NULL, NULL, error);
233			return error;
234		}
235	}
236
237	// wait for the job to finish
238	workerLocker.Unlock();
239	containerLocker.Unlock();
240
241	switch (WaitFor(jobKey)) {
242		case JOB_DEPENDENCY_SUCCEEDED:
243		case JOB_DEPENDENCY_NOT_FOUND:
244			// "Not found" can happen due to a race condition between
245			// unlocking the worker and starting to wait.
246			break;
247		case JOB_DEPENDENCY_ACTIVE:
248			return B_OK;
249		case JOB_DEPENDENCY_FAILED:
250		case JOB_DEPENDENCY_ABORTED:
251		default:
252			return B_ERROR;
253	}
254
255	containerLocker.Lock();
256
257	// now there should be a value for the node
258	nodeResolutionState = parentNode->LocationAndValueResolutionState();
259	return nodeResolutionState != VALUE_NODE_UNRESOLVED
260		? nodeResolutionState : B_ERROR;
261}
262