blob: df55f4e02e0e083573d1e9e6c6016c17b2689503 [file] [log] [blame]
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "debugger.h"
18
Elliott Hughes3bb81562011-10-21 18:52:59 -070019#include <sys/uio.h>
20
Elliott Hughes545a0642011-11-08 19:10:03 -080021#include <set>
22
23#include "class_linker.h"
Elliott Hughes1bba14f2011-12-01 18:00:36 -080024#include "class_loader.h"
Elliott Hughes68fdbd02011-11-29 19:22:47 -080025#include "context.h"
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080026#include "object_utils.h"
Elliott Hughes6a5bd492011-10-28 14:33:57 -070027#include "ScopedLocalRef.h"
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -070028#include "ScopedPrimitiveArray.h"
Elliott Hughes47fce012011-10-25 18:37:19 -070029#include "stack_indirect_reference_table.h"
Elliott Hughes475fc232011-10-25 15:00:35 -070030#include "thread_list.h"
31
Elliott Hughes6a5bd492011-10-28 14:33:57 -070032extern "C" void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*);
33#ifndef HAVE_ANDROID_OS
34void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*) {
35 // No-op for glibc.
36}
37#endif
38
Elliott Hughes872d4ec2011-10-21 17:07:15 -070039namespace art {
40
Elliott Hughes545a0642011-11-08 19:10:03 -080041static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
42static const size_t kNumAllocRecords = 512; // Must be power of 2.
43
Elliott Hughes475fc232011-10-25 15:00:35 -070044class ObjectRegistry {
45 public:
46 ObjectRegistry() : lock_("ObjectRegistry lock") {
47 }
48
49 JDWP::ObjectId Add(Object* o) {
50 if (o == NULL) {
51 return 0;
52 }
53 JDWP::ObjectId id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(o));
54 MutexLock mu(lock_);
55 map_[id] = o;
56 return id;
57 }
58
Elliott Hughes234ab152011-10-26 14:02:26 -070059 void Clear() {
60 MutexLock mu(lock_);
61 LOG(DEBUG) << "Debugger has detached; object registry had " << map_.size() << " entries";
62 map_.clear();
63 }
64
Elliott Hughes475fc232011-10-25 15:00:35 -070065 bool Contains(JDWP::ObjectId id) {
66 MutexLock mu(lock_);
67 return map_.find(id) != map_.end();
68 }
69
Elliott Hughesa2155262011-11-16 16:26:58 -080070 template<typename T> T Get(JDWP::ObjectId id) {
71 MutexLock mu(lock_);
72 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
73 It it = map_.find(id);
74 return (it != map_.end()) ? reinterpret_cast<T>(it->second) : NULL;
75 }
76
Elliott Hughesbfe487b2011-10-26 15:48:55 -070077 void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
78 MutexLock mu(lock_);
79 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
80 for (It it = map_.begin(); it != map_.end(); ++it) {
81 visitor(it->second, arg);
82 }
83 }
84
Elliott Hughes475fc232011-10-25 15:00:35 -070085 private:
86 Mutex lock_;
87 std::map<JDWP::ObjectId, Object*> map_;
88};
89
Elliott Hughes545a0642011-11-08 19:10:03 -080090struct AllocRecordStackTraceElement {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080091 Method* method;
Elliott Hughes545a0642011-11-08 19:10:03 -080092 uintptr_t raw_pc;
93
94 int32_t LineNumber() const {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080095 return MethodHelper(method).GetLineNumFromNativePC(raw_pc);
Elliott Hughes545a0642011-11-08 19:10:03 -080096 }
97};
98
99struct AllocRecord {
100 Class* type;
101 size_t byte_count;
102 uint16_t thin_lock_id;
103 AllocRecordStackTraceElement stack[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
104
105 size_t GetDepth() {
106 size_t depth = 0;
107 while (depth < kMaxAllocRecordStackDepth && stack[depth].method != NULL) {
108 ++depth;
109 }
110 return depth;
111 }
112};
113
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700114// JDWP is allowed unless the Zygote forbids it.
115static bool gJdwpAllowed = true;
116
Elliott Hughes3bb81562011-10-21 18:52:59 -0700117// Was there a -Xrunjdwp or -agent argument on the command-line?
118static bool gJdwpConfigured = false;
119
120// Broken-down JDWP options. (Only valid if gJdwpConfigured is true.)
Elliott Hughes376a7a02011-10-24 18:35:55 -0700121static JDWP::JdwpOptions gJdwpOptions;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700122
123// Runtime JDWP state.
124static JDWP::JdwpState* gJdwpState = NULL;
125static bool gDebuggerConnected; // debugger or DDMS is connected.
126static bool gDebuggerActive; // debugger is making requests.
127
Elliott Hughes47fce012011-10-25 18:37:19 -0700128static bool gDdmThreadNotification = false;
129
Elliott Hughes767a1472011-10-26 18:49:02 -0700130// DDMS GC-related settings.
131static Dbg::HpifWhen gDdmHpifWhen = Dbg::HPIF_WHEN_NEVER;
132static Dbg::HpsgWhen gDdmHpsgWhen = Dbg::HPSG_WHEN_NEVER;
133static Dbg::HpsgWhat gDdmHpsgWhat;
134static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
135static Dbg::HpsgWhat gDdmNhsgWhat;
136
Elliott Hughes475fc232011-10-25 15:00:35 -0700137static ObjectRegistry* gRegistry = NULL;
138
Elliott Hughes545a0642011-11-08 19:10:03 -0800139// Recent allocation tracking.
140static Mutex gAllocTrackerLock("AllocTracker lock");
141AllocRecord* Dbg::recent_allocation_records_ = NULL; // TODO: CircularBuffer<AllocRecord>
142static size_t gAllocRecordHead = 0;
143static size_t gAllocRecordCount = 0;
144
Elliott Hughes24437992011-11-30 14:49:33 -0800145static JDWP::JdwpTag BasicTagFromDescriptor(const char* descriptor) {
146 // JDWP deliberately uses the descriptor characters' ASCII values for its enum.
147 // Note that by "basic" we mean that we don't get more specific than JT_OBJECT.
148 return static_cast<JDWP::JdwpTag>(descriptor[0]);
149}
150
151static JDWP::JdwpTag TagFromClass(Class* c) {
Elliott Hughes86b00102011-12-05 17:54:26 -0800152 CHECK(c != NULL);
Elliott Hughes24437992011-11-30 14:49:33 -0800153 if (c->IsArrayClass()) {
154 return JDWP::JT_ARRAY;
155 }
156
157 if (c->IsStringClass()) {
158 return JDWP::JT_STRING;
159 } else if (c->IsClassClass()) {
160 return JDWP::JT_CLASS_OBJECT;
161#if 0 // TODO
162 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
163 return JDWP::JT_THREAD;
164 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
165 return JDWP::JT_THREAD_GROUP;
166 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
167 return JDWP::JT_CLASS_LOADER;
168#endif
169 } else {
170 return JDWP::JT_OBJECT;
171 }
172}
173
174/*
175 * Objects declared to hold Object might actually hold a more specific
176 * type. The debugger may take a special interest in these (e.g. it
177 * wants to display the contents of Strings), so we want to return an
178 * appropriate tag.
179 *
180 * Null objects are tagged JT_OBJECT.
181 */
182static JDWP::JdwpTag TagFromObject(const Object* o) {
183 return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
184}
185
186static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
187 switch (tag) {
188 case JDWP::JT_BOOLEAN:
189 case JDWP::JT_BYTE:
190 case JDWP::JT_CHAR:
191 case JDWP::JT_FLOAT:
192 case JDWP::JT_DOUBLE:
193 case JDWP::JT_INT:
194 case JDWP::JT_LONG:
195 case JDWP::JT_SHORT:
196 case JDWP::JT_VOID:
197 return true;
198 default:
199 return false;
200 }
201}
202
Elliott Hughes3bb81562011-10-21 18:52:59 -0700203/*
204 * Handle one of the JDWP name/value pairs.
205 *
206 * JDWP options are:
207 * help: if specified, show help message and bail
208 * transport: may be dt_socket or dt_shmem
209 * address: for dt_socket, "host:port", or just "port" when listening
210 * server: if "y", wait for debugger to attach; if "n", attach to debugger
211 * timeout: how long to wait for debugger to connect / listen
212 *
213 * Useful with server=n (these aren't supported yet):
214 * onthrow=<exception-name>: connect to debugger when exception thrown
215 * onuncaught=y|n: connect to debugger when uncaught exception thrown
216 * launch=<command-line>: launch the debugger itself
217 *
218 * The "transport" option is required, as is "address" if server=n.
219 */
220static bool ParseJdwpOption(const std::string& name, const std::string& value) {
221 if (name == "transport") {
222 if (value == "dt_socket") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700223 gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700224 } else if (value == "dt_android_adb") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700225 gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700226 } else {
227 LOG(ERROR) << "JDWP transport not supported: " << value;
228 return false;
229 }
230 } else if (name == "server") {
231 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700232 gJdwpOptions.server = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700233 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700234 gJdwpOptions.server = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700235 } else {
236 LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
237 return false;
238 }
239 } else if (name == "suspend") {
240 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700241 gJdwpOptions.suspend = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700242 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700243 gJdwpOptions.suspend = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700244 } else {
245 LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
246 return false;
247 }
248 } else if (name == "address") {
249 /* this is either <port> or <host>:<port> */
250 std::string port_string;
Elliott Hughes376a7a02011-10-24 18:35:55 -0700251 gJdwpOptions.host.clear();
Elliott Hughes3bb81562011-10-21 18:52:59 -0700252 std::string::size_type colon = value.find(':');
253 if (colon != std::string::npos) {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700254 gJdwpOptions.host = value.substr(0, colon);
Elliott Hughes3bb81562011-10-21 18:52:59 -0700255 port_string = value.substr(colon + 1);
256 } else {
257 port_string = value;
258 }
259 if (port_string.empty()) {
260 LOG(ERROR) << "JDWP address missing port: " << value;
261 return false;
262 }
263 char* end;
264 long port = strtol(port_string.c_str(), &end, 10);
265 if (*end != '\0') {
266 LOG(ERROR) << "JDWP address has junk in port field: " << value;
267 return false;
268 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700269 gJdwpOptions.port = port;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700270 } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
271 /* valid but unsupported */
272 LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
273 } else {
274 LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
275 }
276
277 return true;
278}
279
280/*
281 * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
282 * "transport=dt_socket,address=8000,server=y,suspend=n"
283 */
284bool Dbg::ParseJdwpOptions(const std::string& options) {
Elliott Hughes47fce012011-10-25 18:37:19 -0700285 LOG(VERBOSE) << "ParseJdwpOptions: " << options;
286
Elliott Hughes3bb81562011-10-21 18:52:59 -0700287 std::vector<std::string> pairs;
288 Split(options, ',', pairs);
289
290 for (size_t i = 0; i < pairs.size(); ++i) {
291 std::string::size_type equals = pairs[i].find('=');
292 if (equals == std::string::npos) {
293 LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
294 return false;
295 }
296 ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
297 }
298
Elliott Hughes376a7a02011-10-24 18:35:55 -0700299 if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700300 LOG(ERROR) << "Must specify JDWP transport: " << options;
301 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700302 if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700303 LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
304 return false;
305 }
306
307 gJdwpConfigured = true;
308 return true;
309}
310
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700311void Dbg::StartJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700312 if (!gJdwpAllowed || !gJdwpConfigured) {
313 // No JDWP for you!
314 return;
315 }
316
Elliott Hughes475fc232011-10-25 15:00:35 -0700317 CHECK(gRegistry == NULL);
318 gRegistry = new ObjectRegistry;
319
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700320 // Init JDWP if the debugger is enabled. This may connect out to a
321 // debugger, passively listen for a debugger, or block waiting for a
322 // debugger.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700323 gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
324 if (gJdwpState == NULL) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -0800325 // We probably failed because some other process has the port already, which means that
326 // if we don't abort the user is likely to think they're talking to us when they're actually
327 // talking to that other process.
328 LOG(FATAL) << "debugger thread failed to initialize";
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700329 }
330
331 // If a debugger has already attached, send the "welcome" message.
332 // This may cause us to suspend all threads.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700333 if (gJdwpState->IsActive()) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800334 //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Elliott Hughes376a7a02011-10-24 18:35:55 -0700335 if (!gJdwpState->PostVMStart()) {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700336 LOG(WARNING) << "failed to post 'start' message to debugger";
337 }
338 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700339}
340
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700341void Dbg::StopJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700342 delete gJdwpState;
Elliott Hughes475fc232011-10-25 15:00:35 -0700343 delete gRegistry;
344 gRegistry = NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700345}
346
Elliott Hughes767a1472011-10-26 18:49:02 -0700347void Dbg::GcDidFinish() {
348 if (gDdmHpifWhen != HPIF_WHEN_NEVER) {
349 LOG(DEBUG) << "Sending VM heap info to DDM";
Elliott Hughes7162ad92011-10-27 14:08:42 -0700350 DdmSendHeapInfo(gDdmHpifWhen);
Elliott Hughes767a1472011-10-26 18:49:02 -0700351 }
352 if (gDdmHpsgWhen != HPSG_WHEN_NEVER) {
353 LOG(DEBUG) << "Dumping VM heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700354 DdmSendHeapSegments(false);
Elliott Hughes767a1472011-10-26 18:49:02 -0700355 }
356 if (gDdmNhsgWhen != HPSG_WHEN_NEVER) {
357 LOG(DEBUG) << "Dumping native heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700358 DdmSendHeapSegments(true);
Elliott Hughes767a1472011-10-26 18:49:02 -0700359 }
360}
361
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700362void Dbg::SetJdwpAllowed(bool allowed) {
363 gJdwpAllowed = allowed;
364}
365
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700366DebugInvokeReq* Dbg::GetInvokeReq() {
Elliott Hughes475fc232011-10-25 15:00:35 -0700367 return Thread::Current()->GetInvokeReq();
368}
369
370Thread* Dbg::GetDebugThread() {
371 return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
372}
373
374void Dbg::ClearWaitForEventThread() {
375 gJdwpState->ClearWaitForEventThread();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700376}
377
378void Dbg::Connected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700379 CHECK(!gDebuggerConnected);
380 LOG(VERBOSE) << "JDWP has attached";
381 gDebuggerConnected = true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700382}
383
Elliott Hughesa2155262011-11-16 16:26:58 -0800384void Dbg::GoActive() {
385 // Enable all debugging features, including scans for breakpoints.
386 // This is a no-op if we're already active.
387 // Only called from the JDWP handler thread.
388 if (gDebuggerActive) {
389 return;
390 }
391
392 LOG(INFO) << "Debugger is active";
393
394 // TODO: CHECK we don't have any outstanding breakpoints.
395
396 gDebuggerActive = true;
397
398 //dvmEnableAllSubMode(kSubModeDebuggerActive);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700399}
400
401void Dbg::Disconnected() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700402 CHECK(gDebuggerConnected);
403
404 gDebuggerActive = false;
405
406 //dvmDisableAllSubMode(kSubModeDebuggerActive);
407
408 gRegistry->Clear();
409 gDebuggerConnected = false;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700410}
411
412bool Dbg::IsDebuggerConnected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700413 return gDebuggerActive;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700414}
415
416bool Dbg::IsDebuggingEnabled() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700417 return gJdwpConfigured;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700418}
419
420int64_t Dbg::LastDebuggerActivity() {
Elliott Hughesca951522011-12-05 12:01:32 -0800421 return gJdwpState->LastDebuggerActivity();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700422}
423
424int Dbg::ThreadRunning() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700425 return static_cast<int>(Thread::Current()->SetState(Thread::kRunnable));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700426}
427
428int Dbg::ThreadWaiting() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700429 return static_cast<int>(Thread::Current()->SetState(Thread::kVmWait));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700430}
431
Elliott Hughes6ba581a2011-10-25 11:45:35 -0700432int Dbg::ThreadContinuing(int new_state) {
433 return static_cast<int>(Thread::Current()->SetState(static_cast<Thread::State>(new_state)));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700434}
435
436void Dbg::UndoDebuggerSuspensions() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700437 Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700438}
439
440void Dbg::Exit(int status) {
Elliott Hughes1bba14f2011-12-01 18:00:36 -0800441 exit(status); // This is all dalvik did.
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700442}
443
Elliott Hughesbfe487b2011-10-26 15:48:55 -0700444void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
445 if (gRegistry != NULL) {
446 gRegistry->VisitRoots(visitor, arg);
447 }
448}
449
Elliott Hughesa2155262011-11-16 16:26:58 -0800450std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
451 Class* c = gRegistry->Get<Class*>(classId);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800452 return ClassHelper(c).GetDescriptor();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700453}
454
455JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
456 UNIMPLEMENTED(FATAL);
457 return 0;
458}
459
460JDWP::RefTypeId Dbg::GetSuperclass(JDWP::RefTypeId id) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800461 Class* c = gRegistry->Get<Class*>(id);
462 return gRegistry->Add(c->GetSuperClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700463}
464
465JDWP::ObjectId Dbg::GetClassLoader(JDWP::RefTypeId id) {
Elliott Hughes1bba14f2011-12-01 18:00:36 -0800466 Object* o = gRegistry->Get<Object*>(id);
467 return gRegistry->Add(o->GetClass()->GetClassLoader());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700468}
469
470uint32_t Dbg::GetAccessFlags(JDWP::RefTypeId id) {
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800471 Class* c = gRegistry->Get<Class*>(id);
472 return c->GetAccessFlags() & kAccJavaFlagsMask;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700473}
474
Elliott Hughesaed4be92011-12-02 16:16:23 -0800475bool Dbg::IsInterface(JDWP::RefTypeId classId) {
476 Class* c = gRegistry->Get<Class*>(classId);
477 return c->IsInterface();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700478}
479
Elliott Hughesa2155262011-11-16 16:26:58 -0800480void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
481 // Get the complete list of reference classes (i.e. all classes except
482 // the primitive types).
483 // Returns a newly-allocated buffer full of RefTypeId values.
484 struct ClassListCreator {
485 static bool Visit(Class* c, void* arg) {
486 return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
487 }
488
489 bool Visit(Class* c) {
490 if (!c->IsPrimitive()) {
491 classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
492 }
493 return true;
494 }
495
496 std::vector<JDWP::RefTypeId> classes;
497 };
498
499 ClassListCreator clc;
500 Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
501 *pClassCount = clc.classes.size();
502 *pClasses = new JDWP::RefTypeId[clc.classes.size()];
503 for (size_t i = 0; i < clc.classes.size(); ++i) {
504 (*pClasses)[i] = clc.classes[i];
505 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700506}
507
508void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
509 UNIMPLEMENTED(FATAL);
510}
511
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800512void Dbg::GetClassInfo(JDWP::RefTypeId classId, JDWP::JdwpTypeTag* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800513 Class* c = gRegistry->Get<Class*>(classId);
514 if (c->IsArrayClass()) {
515 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
516 *pTypeTag = JDWP::TT_ARRAY;
517 } else {
518 if (c->IsErroneous()) {
519 *pStatus = JDWP::CS_ERROR;
520 } else {
521 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
522 }
523 *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
524 }
525
526 if (pDescriptor != NULL) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800527 *pDescriptor = ClassHelper(c).GetDescriptor();
Elliott Hughesa2155262011-11-16 16:26:58 -0800528 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700529}
530
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800531void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids) {
532 std::vector<Class*> classes;
533 Runtime::Current()->GetClassLinker()->LookupClasses(descriptor, classes);
534 ids.clear();
535 for (size_t i = 0; i < classes.size(); ++i) {
536 ids.push_back(gRegistry->Add(classes[i]));
537 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700538}
539
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800540void Dbg::GetObjectType(JDWP::ObjectId objectId, JDWP::JdwpTypeTag* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800541 Object* o = gRegistry->Get<Object*>(objectId);
542 if (o->GetClass()->IsArrayClass()) {
543 *pRefTypeTag = JDWP::TT_ARRAY;
544 } else if (o->GetClass()->IsInterface()) {
545 *pRefTypeTag = JDWP::TT_INTERFACE;
546 } else {
547 *pRefTypeTag = JDWP::TT_CLASS;
548 }
549 *pRefTypeId = gRegistry->Add(o->GetClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700550}
551
552uint8_t Dbg::GetClassObjectType(JDWP::RefTypeId refTypeId) {
553 UNIMPLEMENTED(FATAL);
554 return 0;
555}
556
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800557std::string Dbg::GetSignature(JDWP::RefTypeId refTypeId) {
558 Class* c = gRegistry->Get<Class*>(refTypeId);
559 CHECK(c != NULL);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800560 return ClassHelper(c).GetDescriptor();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700561}
562
Elliott Hughes03181a82011-11-17 17:22:21 -0800563bool Dbg::GetSourceFile(JDWP::RefTypeId refTypeId, std::string& result) {
564 Class* c = gRegistry->Get<Class*>(refTypeId);
565 CHECK(c != NULL);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800566 result = ClassHelper(c).GetSourceFile();
567 return result == NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700568}
569
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700570uint8_t Dbg::GetObjectTag(JDWP::ObjectId objectId) {
Elliott Hughes24437992011-11-30 14:49:33 -0800571 Object* o = gRegistry->Get<Object*>(objectId);
572 return TagFromObject(o);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700573}
574
Elliott Hughesaed4be92011-12-02 16:16:23 -0800575size_t Dbg::GetTagWidth(JDWP::JdwpTag tag) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800576 switch (tag) {
577 case JDWP::JT_VOID:
578 return 0;
579 case JDWP::JT_BYTE:
580 case JDWP::JT_BOOLEAN:
581 return 1;
582 case JDWP::JT_CHAR:
583 case JDWP::JT_SHORT:
584 return 2;
585 case JDWP::JT_FLOAT:
586 case JDWP::JT_INT:
587 return 4;
588 case JDWP::JT_ARRAY:
589 case JDWP::JT_OBJECT:
590 case JDWP::JT_STRING:
591 case JDWP::JT_THREAD:
592 case JDWP::JT_THREAD_GROUP:
593 case JDWP::JT_CLASS_LOADER:
594 case JDWP::JT_CLASS_OBJECT:
595 return sizeof(JDWP::ObjectId);
596 case JDWP::JT_DOUBLE:
597 case JDWP::JT_LONG:
598 return 8;
599 default:
600 LOG(FATAL) << "unknown tag " << tag;
601 return -1;
602 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700603}
604
605int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800606 Object* o = gRegistry->Get<Object*>(arrayId);
607 Array* a = o->AsArray();
608 return a->GetLength();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700609}
610
611uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
Elliott Hughes24437992011-11-30 14:49:33 -0800612 Object* o = gRegistry->Get<Object*>(arrayId);
613 Array* a = o->AsArray();
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800614 std::string descriptor(ClassHelper(a->GetClass()).GetDescriptor());
Elliott Hughes24437992011-11-30 14:49:33 -0800615 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
616 if (!IsPrimitiveTag(tag)) {
617 tag = TagFromClass(a->GetClass()->GetComponentType());
618 }
619 return tag;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700620}
621
Elliott Hughes24437992011-11-30 14:49:33 -0800622bool Dbg::OutputArray(JDWP::ObjectId arrayId, int offset, int count, JDWP::ExpandBuf* pReply) {
623 Object* o = gRegistry->Get<Object*>(arrayId);
624 Array* a = o->AsArray();
625
626 if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
627 LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
628 return false;
629 }
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800630 std::string descriptor(ClassHelper(a->GetClass()).GetDescriptor());
Elliott Hughes24437992011-11-30 14:49:33 -0800631 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
632
633 if (IsPrimitiveTag(tag)) {
634 size_t width = GetTagWidth(tag);
635 const uint8_t* src = reinterpret_cast<uint8_t*>(a->GetRawData());
636 uint8_t* dst = expandBufAddSpace(pReply, count * width);
637 if (width == 8) {
638 const uint64_t* src8 = reinterpret_cast<const uint64_t*>(src);
639 for (int i = 0; i < count; ++i) JDWP::Write8BE(&dst, src8[offset + i]);
640 } else if (width == 4) {
641 const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
642 for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[offset + i]);
643 } else if (width == 2) {
644 const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
645 for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[offset + i]);
646 } else {
647 memcpy(dst, &src[offset * width], count * width);
648 }
649 } else {
650 ObjectArray<Object>* oa = a->AsObjectArray<Object>();
651 for (int i = 0; i < count; ++i) {
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800652 Object* element = oa->Get(offset + i);
Elliott Hughes24437992011-11-30 14:49:33 -0800653 JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
654 expandBufAdd1(pReply, specific_tag);
655 expandBufAddObjectId(pReply, gRegistry->Add(element));
656 }
657 }
658
659 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700660}
661
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800662bool Dbg::SetArrayElements(JDWP::ObjectId arrayId, int offset, int count, const uint8_t* src) {
663 Object* o = gRegistry->Get<Object*>(arrayId);
664 Array* a = o->AsArray();
665
666 if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
667 LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
668 return false;
669 }
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800670 std::string descriptor(ClassHelper(a->GetClass()).GetDescriptor());
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800671 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
672
673 if (IsPrimitiveTag(tag)) {
674 size_t width = GetTagWidth(tag);
675 uint8_t* dst = &(reinterpret_cast<uint8_t*>(a->GetRawData())[offset * width]);
676 if (width == 8) {
677 for (int i = 0; i < count; ++i) {
678 // Handle potentially non-aligned memory access one byte at a time for ARM's benefit.
679 uint64_t value;
680 for (size_t j = 0; j < sizeof(uint64_t); ++j) reinterpret_cast<uint8_t*>(&value)[j] = src[j];
681 src += sizeof(uint64_t);
682 JDWP::Write8BE(&dst, value);
683 }
684 } else if (width == 4) {
685 const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
686 for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[i]);
687 } else if (width == 2) {
688 const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
689 for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[i]);
690 } else {
691 memcpy(&dst[offset * width], src, count * width);
692 }
693 } else {
694 ObjectArray<Object>* oa = a->AsObjectArray<Object>();
695 for (int i = 0; i < count; ++i) {
696 JDWP::ObjectId id = JDWP::ReadObjectId(&src);
697 oa->Set(offset + i, gRegistry->Get<Object*>(id));
698 }
699 }
700
701 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700702}
703
704JDWP::ObjectId Dbg::CreateString(const char* str) {
Elliott Hughescccd84f2011-12-05 16:51:54 -0800705 return gRegistry->Add(String::AllocFromModifiedUtf8(str));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700706}
707
708JDWP::ObjectId Dbg::CreateObject(JDWP::RefTypeId classId) {
Elliott Hughescccd84f2011-12-05 16:51:54 -0800709 Class* c = gRegistry->Get<Class*>(classId);
710 return gRegistry->Add(c->AllocObject());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700711}
712
713JDWP::ObjectId Dbg::CreateArrayObject(JDWP::RefTypeId arrayTypeId, uint32_t length) {
714 UNIMPLEMENTED(FATAL);
715 return 0;
716}
717
718bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
Elliott Hughesd07986f2011-12-06 18:27:45 -0800719 return gRegistry->Get<Class*>(instClassId)->InstanceOf(gRegistry->Get<Class*>(classId));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700720}
721
Elliott Hughes03181a82011-11-17 17:22:21 -0800722JDWP::FieldId ToFieldId(Field* f) {
723#ifdef MOVING_GARBAGE_COLLECTOR
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700724 UNIMPLEMENTED(FATAL);
Elliott Hughes03181a82011-11-17 17:22:21 -0800725#else
726 return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
727#endif
728}
729
730JDWP::MethodId ToMethodId(Method* m) {
731#ifdef MOVING_GARBAGE_COLLECTOR
732 UNIMPLEMENTED(FATAL);
733#else
734 return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
735#endif
736}
737
Elliott Hughesaed4be92011-12-02 16:16:23 -0800738Field* FromFieldId(JDWP::FieldId fid) {
739#ifdef MOVING_GARBAGE_COLLECTOR
740 UNIMPLEMENTED(FATAL);
741#else
742 return reinterpret_cast<Field*>(static_cast<uintptr_t>(fid));
743#endif
744}
745
Elliott Hughes03181a82011-11-17 17:22:21 -0800746Method* FromMethodId(JDWP::MethodId mid) {
747#ifdef MOVING_GARBAGE_COLLECTOR
748 UNIMPLEMENTED(FATAL);
749#else
750 return reinterpret_cast<Method*>(static_cast<uintptr_t>(mid));
751#endif
752}
753
Elliott Hughesd07986f2011-12-06 18:27:45 -0800754void SetLocation(JDWP::JdwpLocation& location, Method* m, uintptr_t native_pc) {
755 Class* c = m->GetDeclaringClass();
756 location.typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
757 location.classId = gRegistry->Add(c);
758 location.methodId = ToMethodId(m);
759 location.idx = m->IsNative() ? -1 : m->ToDexPC(native_pc);
760}
761
Elliott Hughes03181a82011-11-17 17:22:21 -0800762std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800763 Method* m = FromMethodId(methodId);
764 return MethodHelper(m).GetName();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700765}
766
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800767/*
768 * Augment the access flags for synthetic methods and fields by setting
769 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
770 * flags not specified by the Java programming language.
771 */
772static uint32_t MangleAccessFlags(uint32_t accessFlags) {
773 accessFlags &= kAccJavaFlagsMask;
774 if ((accessFlags & kAccSynthetic) != 0) {
775 accessFlags |= 0xf0000000;
776 }
777 return accessFlags;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700778}
779
Elliott Hughesdbb40792011-11-18 17:05:22 -0800780static const uint16_t kEclipseWorkaroundSlot = 1000;
781
782/*
783 * Eclipse appears to expect that the "this" reference is in slot zero.
784 * If it's not, the "variables" display will show two copies of "this",
785 * possibly because it gets "this" from SF.ThisObject and then displays
786 * all locals with nonzero slot numbers.
787 *
788 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
789 * SF.GetValues / SF.SetValues we map them back.
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800790 *
791 * TODO: jdb uses the value to determine whether a variable is a local or an argument,
792 * by checking whether it's less than the number of arguments. To make that work, we'd
793 * have to "mangle" all the arguments to come first, not just the implicit argument 'this'.
Elliott Hughesdbb40792011-11-18 17:05:22 -0800794 */
795static uint16_t MangleSlot(uint16_t slot, const char* name) {
796 uint16_t newSlot = slot;
797 if (strcmp(name, "this") == 0) {
798 newSlot = 0;
799 } else if (slot == 0) {
800 newSlot = kEclipseWorkaroundSlot;
801 }
802 return newSlot;
803}
804
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800805static uint16_t DemangleSlot(uint16_t slot, Frame& f) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800806 if (slot == kEclipseWorkaroundSlot) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800807 return 0;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800808 } else if (slot == 0) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800809 const DexFile::CodeItem* code_item = MethodHelper(f.GetMethod()).GetCodeItem();
810 return code_item->registers_size_ - code_item->ins_size_;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800811 }
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800812 return slot;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800813}
814
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800815void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800816 Class* c = gRegistry->Get<Class*>(refTypeId);
817 CHECK(c != NULL);
818
819 size_t instance_field_count = c->NumInstanceFields();
820 size_t static_field_count = c->NumStaticFields();
821
822 expandBufAdd4BE(pReply, instance_field_count + static_field_count);
823
824 for (size_t i = 0; i < instance_field_count + static_field_count; ++i) {
825 Field* f = (i < instance_field_count) ? c->GetInstanceField(i) : c->GetStaticField(i - instance_field_count);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800826 FieldHelper fh(f);
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800827 expandBufAddFieldId(pReply, ToFieldId(f));
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800828 expandBufAddUtf8String(pReply, fh.GetName());
829 expandBufAddUtf8String(pReply, fh.GetTypeDescriptor());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800830 if (with_generic) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800831 static const char genericSignature[1] = "";
832 expandBufAddUtf8String(pReply, genericSignature);
833 }
834 expandBufAdd4BE(pReply, MangleAccessFlags(f->GetAccessFlags()));
835 }
836}
837
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800838void Dbg::OutputDeclaredMethods(JDWP::RefTypeId refTypeId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800839 Class* c = gRegistry->Get<Class*>(refTypeId);
840 CHECK(c != NULL);
841
842 size_t direct_method_count = c->NumDirectMethods();
843 size_t virtual_method_count = c->NumVirtualMethods();
844
845 expandBufAdd4BE(pReply, direct_method_count + virtual_method_count);
846
847 for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) {
848 Method* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800849 MethodHelper mh(m);
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800850 expandBufAddMethodId(pReply, ToMethodId(m));
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800851 expandBufAddUtf8String(pReply, mh.GetName());
852 expandBufAddUtf8String(pReply, mh.GetSignature().c_str());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800853 if (with_generic) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800854 static const char genericSignature[1] = "";
855 expandBufAddUtf8String(pReply, genericSignature);
856 }
857 expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags()));
858 }
859}
860
861void Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId refTypeId, JDWP::ExpandBuf* pReply) {
862 Class* c = gRegistry->Get<Class*>(refTypeId);
863 CHECK(c != NULL);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800864 ClassHelper kh(c);
865 size_t interface_count = kh.NumInterfaces();
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800866 expandBufAdd4BE(pReply, interface_count);
867 for (size_t i = 0; i < interface_count; ++i) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800868 expandBufAddRefTypeId(pReply, gRegistry->Add(kh.GetInterface(i)));
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800869 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700870}
871
872void Dbg::OutputLineTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, JDWP::ExpandBuf* pReply) {
Elliott Hughes03181a82011-11-17 17:22:21 -0800873 struct DebugCallbackContext {
874 int numItems;
875 JDWP::ExpandBuf* pReply;
876
877 static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
878 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
879 expandBufAdd8BE(pContext->pReply, address);
880 expandBufAdd4BE(pContext->pReply, lineNum);
881 pContext->numItems++;
882 return true;
883 }
884 };
885
886 Method* m = FromMethodId(methodId);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800887 MethodHelper mh(m);
Elliott Hughes03181a82011-11-17 17:22:21 -0800888 uint64_t start, end;
889 if (m->IsNative()) {
890 start = -1;
891 end = -1;
892 } else {
893 start = 0;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800894 // TODO: what are the units supposed to be? *2?
895 end = mh.GetCodeItem()->insns_size_in_code_units_;
Elliott Hughes03181a82011-11-17 17:22:21 -0800896 }
897
898 expandBufAdd8BE(pReply, start);
899 expandBufAdd8BE(pReply, end);
900
901 // Add numLines later
902 size_t numLinesOffset = expandBufGetLength(pReply);
903 expandBufAdd4BE(pReply, 0);
904
905 DebugCallbackContext context;
906 context.numItems = 0;
907 context.pReply = pReply;
908
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800909 mh.GetDexFile().DecodeDebugInfo(mh.GetCodeItem(), m->IsStatic(), m->GetDexMethodIndex(),
910 DebugCallbackContext::Callback, NULL, &context);
Elliott Hughes03181a82011-11-17 17:22:21 -0800911
912 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700913}
914
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800915void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800916 struct DebugCallbackContext {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800917 JDWP::ExpandBuf* pReply;
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800918 size_t variable_count;
919 bool with_generic;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800920
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800921 static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char* name, const char* descriptor, const char* signature) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800922 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
923
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800924 LOG(VERBOSE) << StringPrintf(" %2d: %d(%d) '%s' '%s' '%s' slot=%d", pContext->variable_count, startAddress, endAddress - startAddress, name, descriptor, signature, slot);
Elliott Hughesdbb40792011-11-18 17:05:22 -0800925
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800926 slot = MangleSlot(slot, name);
927
Elliott Hughesdbb40792011-11-18 17:05:22 -0800928 expandBufAdd8BE(pContext->pReply, startAddress);
929 expandBufAddUtf8String(pContext->pReply, name);
930 expandBufAddUtf8String(pContext->pReply, descriptor);
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800931 if (pContext->with_generic) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800932 expandBufAddUtf8String(pContext->pReply, signature);
933 }
934 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
935 expandBufAdd4BE(pContext->pReply, slot);
936
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800937 ++pContext->variable_count;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800938 }
939 };
940
941 Method* m = FromMethodId(methodId);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800942 MethodHelper mh(m);
943 const DexFile::CodeItem* code_item = mh.GetCodeItem();
Elliott Hughesdbb40792011-11-18 17:05:22 -0800944
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800945 // arg_count considers doubles and longs to take 2 units.
946 // variable_count considers everything to take 1 unit.
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800947 std::string shorty(mh.GetShorty());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800948 expandBufAdd4BE(pReply, m->NumArgRegisters(shorty));
Elliott Hughesdbb40792011-11-18 17:05:22 -0800949
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800950 // We don't know the total number of variables yet, so leave a blank and update it later.
951 size_t variable_count_offset = expandBufGetLength(pReply);
Elliott Hughesdbb40792011-11-18 17:05:22 -0800952 expandBufAdd4BE(pReply, 0);
953
954 DebugCallbackContext context;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800955 context.pReply = pReply;
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800956 context.variable_count = 0;
957 context.with_generic = with_generic;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800958
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800959 mh.GetDexFile().DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(), NULL,
960 DebugCallbackContext::Callback, &context);
Elliott Hughesdbb40792011-11-18 17:05:22 -0800961
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800962 JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700963}
964
Elliott Hughesaed4be92011-12-02 16:16:23 -0800965JDWP::JdwpTag Dbg::GetFieldBasicTag(JDWP::FieldId fieldId) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800966 return BasicTagFromDescriptor(FieldHelper(FromFieldId(fieldId)).GetTypeDescriptor());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700967}
968
Elliott Hughesaed4be92011-12-02 16:16:23 -0800969JDWP::JdwpTag Dbg::GetStaticFieldBasicTag(JDWP::FieldId fieldId) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800970 return BasicTagFromDescriptor(FieldHelper(FromFieldId(fieldId)).GetTypeDescriptor());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700971}
972
973void Dbg::GetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
Elliott Hughesaed4be92011-12-02 16:16:23 -0800974 Object* o = gRegistry->Get<Object*>(objectId);
975 Field* f = FromFieldId(fieldId);
976
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800977 JDWP::JdwpTag tag = BasicTagFromDescriptor(FieldHelper(f).GetTypeDescriptor());
Elliott Hughesaed4be92011-12-02 16:16:23 -0800978
979 if (IsPrimitiveTag(tag)) {
980 expandBufAdd1(pReply, tag);
981 if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) {
982 expandBufAdd1(pReply, f->Get32(o));
983 } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) {
984 expandBufAdd2BE(pReply, f->Get32(o));
985 } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) {
986 expandBufAdd4BE(pReply, f->Get32(o));
987 } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
988 expandBufAdd8BE(pReply, f->Get64(o));
989 } else {
990 LOG(FATAL) << "unknown tag: " << tag;
991 }
992 } else {
993 Object* value = f->GetObject(o);
994 expandBufAdd1(pReply, TagFromObject(value));
995 expandBufAddObjectId(pReply, gRegistry->Add(value));
996 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700997}
998
999void Dbg::SetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, uint64_t value, int width) {
Elliott Hughesaed4be92011-12-02 16:16:23 -08001000 Object* o = gRegistry->Get<Object*>(objectId);
1001 Field* f = FromFieldId(fieldId);
1002
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08001003 JDWP::JdwpTag tag = BasicTagFromDescriptor(FieldHelper(f).GetTypeDescriptor());
Elliott Hughesaed4be92011-12-02 16:16:23 -08001004
1005 if (IsPrimitiveTag(tag)) {
1006 if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
1007 f->Set64(o, value);
1008 } else {
1009 f->Set32(o, value);
1010 }
1011 } else {
1012 f->SetObject(o, gRegistry->Get<Object*>(value));
1013 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001014}
1015
Elliott Hughes6fa602d2011-12-02 17:54:25 -08001016void Dbg::GetStaticFieldValue(JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
1017 GetFieldValue(0, fieldId, pReply);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001018}
1019
Elliott Hughes6fa602d2011-12-02 17:54:25 -08001020void Dbg::SetStaticFieldValue(JDWP::FieldId fieldId, uint64_t value, int width) {
1021 SetFieldValue(0, fieldId, value, width);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001022}
1023
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001024std::string Dbg::StringToUtf8(JDWP::ObjectId strId) {
1025 String* s = gRegistry->Get<String*>(strId);
1026 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001027}
1028
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001029Thread* DecodeThread(JDWP::ObjectId threadId) {
1030 Object* thread_peer = gRegistry->Get<Object*>(threadId);
1031 CHECK(thread_peer != NULL);
1032 return Thread::FromManagedThread(thread_peer);
1033}
1034
1035bool Dbg::GetThreadName(JDWP::ObjectId threadId, std::string& name) {
1036 ScopedThreadListLock thread_list_lock;
1037 Thread* thread = DecodeThread(threadId);
1038 if (thread == NULL) {
1039 return false;
1040 }
1041 StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetName()->ToModifiedUtf8().c_str());
1042 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001043}
1044
1045JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
Elliott Hughes499c5132011-11-17 14:55:11 -08001046 Object* thread = gRegistry->Get<Object*>(threadId);
1047 CHECK(thread != NULL);
1048
1049 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
1050 CHECK(c != NULL);
1051 Field* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
1052 CHECK(f != NULL);
1053 Object* group = f->GetObject(thread);
1054 CHECK(group != NULL);
1055 return gRegistry->Add(group);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001056}
1057
Elliott Hughes499c5132011-11-17 14:55:11 -08001058std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
1059 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
1060 CHECK(thread_group != NULL);
1061
1062 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1063 CHECK(c != NULL);
1064 Field* f = c->FindInstanceField("name", "Ljava/lang/String;");
1065 CHECK(f != NULL);
1066 String* s = reinterpret_cast<String*>(f->GetObject(thread_group));
1067 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001068}
1069
1070JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001071 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
1072 CHECK(thread_group != NULL);
1073
1074 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1075 CHECK(c != NULL);
1076 Field* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
1077 CHECK(f != NULL);
1078 Object* parent = f->GetObject(thread_group);
1079 return gRegistry->Add(parent);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001080}
1081
Elliott Hughes499c5132011-11-17 14:55:11 -08001082static Object* GetStaticThreadGroup(const char* field_name) {
1083 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1084 CHECK(c != NULL);
1085 Field* f = c->FindStaticField(field_name, "Ljava/lang/ThreadGroup;");
1086 CHECK(f != NULL);
1087 Object* group = f->GetObject(NULL);
1088 CHECK(group != NULL);
1089 return group;
1090}
1091
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001092JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -08001093 return gRegistry->Add(GetStaticThreadGroup("mSystem"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001094}
1095
1096JDWP::ObjectId Dbg::GetMainThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -08001097 return gRegistry->Add(GetStaticThreadGroup("mMain"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001098}
1099
Elliott Hughes499c5132011-11-17 14:55:11 -08001100bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* pThreadStatus, uint32_t* pSuspendStatus) {
1101 ScopedThreadListLock thread_list_lock;
1102
1103 Thread* thread = DecodeThread(threadId);
1104 if (thread == NULL) {
1105 return false;
1106 }
1107
1108 switch (thread->GetState()) {
1109 case Thread::kTerminated: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1110 case Thread::kRunnable: *pThreadStatus = JDWP::TS_RUNNING; break;
1111 case Thread::kTimedWaiting: *pThreadStatus = JDWP::TS_SLEEPING; break;
1112 case Thread::kBlocked: *pThreadStatus = JDWP::TS_MONITOR; break;
1113 case Thread::kWaiting: *pThreadStatus = JDWP::TS_WAIT; break;
1114 case Thread::kInitializing: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1115 case Thread::kStarting: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1116 case Thread::kNative: *pThreadStatus = JDWP::TS_RUNNING; break;
1117 case Thread::kVmWait: *pThreadStatus = JDWP::TS_WAIT; break;
1118 case Thread::kSuspended: *pThreadStatus = JDWP::TS_RUNNING; break;
1119 default:
1120 LOG(FATAL) << "unknown thread state " << thread->GetState();
1121 }
1122
1123 *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : 0);
1124
1125 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001126}
1127
1128uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
Elliott Hughesd07986f2011-12-06 18:27:45 -08001129 return DecodeThread(threadId)->GetSuspendCount();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001130}
1131
1132bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -08001133 return DecodeThread(threadId) != NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001134}
1135
1136bool Dbg::IsSuspended(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -08001137 return DecodeThread(threadId)->IsSuspended();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001138}
1139
Elliott Hughesa2155262011-11-16 16:26:58 -08001140void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
1141 struct ThreadListVisitor {
1142 static void Visit(Thread* t, void* arg) {
1143 reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
1144 }
1145
1146 void Visit(Thread* t) {
1147 if (t == Dbg::GetDebugThread()) {
1148 // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
1149 // query all threads, so it's easier if we just don't tell them about this thread.
1150 return;
1151 }
1152 if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
1153 threads.push_back(gRegistry->Add(t->GetPeer()));
1154 }
1155 }
1156
1157 Object* thread_group;
1158 std::vector<JDWP::ObjectId> threads;
1159 };
1160
1161 ThreadListVisitor tlv;
1162 tlv.thread_group = thread_group;
1163
1164 {
1165 ScopedThreadListLock thread_list_lock;
1166 Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
1167 }
1168
1169 *pThreadCount = tlv.threads.size();
1170 if (*pThreadCount == 0) {
1171 *ppThreadIds = NULL;
1172 } else {
1173 *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
1174 for (size_t i = 0; i < *pThreadCount; ++i) {
1175 (*ppThreadIds)[i] = tlv.threads[i];
1176 }
1177 }
1178}
1179
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001180void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001181 GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001182}
1183
1184void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001185 GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001186}
1187
1188int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
Elliott Hughes03181a82011-11-17 17:22:21 -08001189 ScopedThreadListLock thread_list_lock;
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001190 struct CountStackDepthVisitor : public Thread::StackVisitor {
1191 CountStackDepthVisitor() : depth(0) {}
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001192 virtual void VisitFrame(const Frame& f, uintptr_t) {
1193 // TODO: we'll need to skip callee-save frames too.
1194 if (f.HasMethod()) {
1195 ++depth;
1196 }
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001197 }
1198 size_t depth;
1199 };
1200 CountStackDepthVisitor visitor;
1201 DecodeThread(threadId)->WalkStack(&visitor);
1202 return visitor.depth;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001203}
1204
Elliott Hughes03181a82011-11-17 17:22:21 -08001205bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
1206 ScopedThreadListLock thread_list_lock;
1207 struct GetFrameVisitor : public Thread::StackVisitor {
1208 GetFrameVisitor(int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc)
1209 : found(false) ,depth(0), desired_frame_number(desired_frame_number), pFrameId(pFrameId), pLoc(pLoc) {
1210 }
1211 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001212 // TODO: we'll need to skip callee-save frames too.
Elliott Hughes03181a82011-11-17 17:22:21 -08001213 if (!f.HasMethod()) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001214 return; // The debugger can't do anything useful with a frame that has no Method*.
Elliott Hughes03181a82011-11-17 17:22:21 -08001215 }
1216
1217 if (depth == desired_frame_number) {
1218 *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
Elliott Hughesd07986f2011-12-06 18:27:45 -08001219 SetLocation(*pLoc, f.GetMethod(), pc);
Elliott Hughes03181a82011-11-17 17:22:21 -08001220 found = true;
1221 }
1222 ++depth;
1223 }
1224 bool found;
1225 int depth;
1226 int desired_frame_number;
1227 JDWP::FrameId* pFrameId;
1228 JDWP::JdwpLocation* pLoc;
1229 };
1230 GetFrameVisitor visitor(desired_frame_number, pFrameId, pLoc);
1231 visitor.desired_frame_number = desired_frame_number;
1232 DecodeThread(threadId)->WalkStack(&visitor);
1233 return visitor.found;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001234}
1235
1236JDWP::ObjectId Dbg::GetThreadSelfId() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001237 return gRegistry->Add(Thread::Current()->GetPeer());
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001238}
1239
Elliott Hughes475fc232011-10-25 15:00:35 -07001240void Dbg::SuspendVM() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001241 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable); // TODO: do we really want to change back? should the JDWP thread be Runnable usually?
Elliott Hughes475fc232011-10-25 15:00:35 -07001242 Runtime::Current()->GetThreadList()->SuspendAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001243}
1244
1245void Dbg::ResumeVM() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001246 Runtime::Current()->GetThreadList()->ResumeAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001247}
1248
1249void Dbg::SuspendThread(JDWP::ObjectId threadId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001250 Object* peer = gRegistry->Get<Object*>(threadId);
1251 ScopedThreadListLock thread_list_lock;
1252 Thread* thread = Thread::FromManagedThread(peer);
1253 if (thread == NULL) {
1254 LOG(WARNING) << "No such thread for suspend: " << peer;
1255 return;
1256 }
1257 Runtime::Current()->GetThreadList()->Suspend(thread, true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001258}
1259
1260void Dbg::ResumeThread(JDWP::ObjectId threadId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001261 Object* peer = gRegistry->Get<Object*>(threadId);
1262 ScopedThreadListLock thread_list_lock;
1263 Thread* thread = Thread::FromManagedThread(peer);
1264 if (thread == NULL) {
1265 LOG(WARNING) << "No such thread for resume: " << peer;
1266 return;
1267 }
1268 Runtime::Current()->GetThreadList()->Resume(thread, true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001269}
1270
1271void Dbg::SuspendSelf() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001272 Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001273}
1274
Elliott Hughesd07986f2011-12-06 18:27:45 -08001275bool Dbg::GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
Elliott Hughes86b00102011-12-05 17:54:26 -08001276 Method** sp = reinterpret_cast<Method**>(frameId);
1277 Frame f;
1278 f.SetSP(sp);
Elliott Hughes86b00102011-12-05 17:54:26 -08001279 Method* m = f.GetMethod();
1280
1281 Object* o = NULL;
1282 if (!m->IsNative() && !m->IsStatic()) {
Elliott Hughesd07986f2011-12-06 18:27:45 -08001283 uint16_t reg = DemangleSlot(0, f);
Elliott Hughes86b00102011-12-05 17:54:26 -08001284 o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
1285 }
1286 *pThisId = gRegistry->Add(o);
1287 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001288}
1289
Elliott Hughescccd84f2011-12-05 16:51:54 -08001290void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001291 Method** sp = reinterpret_cast<Method**>(frameId);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001292 Frame f;
1293 f.SetSP(sp);
1294 uint16_t reg = DemangleSlot(slot, f);
1295 Method* m = f.GetMethod();
1296
1297 const VmapTable vmap_table(m->GetVmapTableRaw());
1298 uint32_t vmap_offset;
1299 if (vmap_table.IsInContext(reg, vmap_offset)) {
1300 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1301 }
Elliott Hughesdbb40792011-11-18 17:05:22 -08001302
1303 switch (tag) {
1304 case JDWP::JT_BOOLEAN:
1305 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001306 CHECK_EQ(width, 1U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001307 uint32_t intVal = f.GetVReg(m, reg);
1308 LOG(VERBOSE) << "get boolean local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001309 JDWP::Set1(buf+1, intVal != 0);
1310 }
1311 break;
1312 case JDWP::JT_BYTE:
1313 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001314 CHECK_EQ(width, 1U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001315 uint32_t intVal = f.GetVReg(m, reg);
1316 LOG(VERBOSE) << "get byte local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001317 JDWP::Set1(buf+1, intVal);
1318 }
1319 break;
1320 case JDWP::JT_SHORT:
1321 case JDWP::JT_CHAR:
1322 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001323 CHECK_EQ(width, 2U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001324 uint32_t intVal = f.GetVReg(m, reg);
1325 LOG(VERBOSE) << "get short/char local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001326 JDWP::Set2BE(buf+1, intVal);
1327 }
1328 break;
1329 case JDWP::JT_INT:
1330 case JDWP::JT_FLOAT:
1331 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001332 CHECK_EQ(width, 4U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001333 uint32_t intVal = f.GetVReg(m, reg);
1334 LOG(VERBOSE) << "get int/float local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001335 JDWP::Set4BE(buf+1, intVal);
1336 }
1337 break;
1338 case JDWP::JT_ARRAY:
1339 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001340 CHECK_EQ(width, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001341 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001342 LOG(VERBOSE) << "get array local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001343 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001344 LOG(FATAL) << "reg " << reg << " expected to hold array: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001345 }
1346 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1347 }
1348 break;
1349 case JDWP::JT_OBJECT:
1350 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001351 CHECK_EQ(width, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001352 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001353 LOG(VERBOSE) << "get object local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001354 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001355 LOG(FATAL) << "reg " << reg << " expected to hold object: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001356 }
1357 tag = TagFromObject(o);
1358 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1359 }
1360 break;
1361 case JDWP::JT_DOUBLE:
1362 case JDWP::JT_LONG:
1363 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001364 CHECK_EQ(width, 8U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001365 uint32_t lo = f.GetVReg(m, reg);
1366 uint64_t hi = f.GetVReg(m, reg + 1);
1367 uint64_t longVal = (hi << 32) | lo;
1368 LOG(VERBOSE) << "get double/long local " << hi << ":" << lo << " = " << longVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001369 JDWP::Set8BE(buf+1, longVal);
1370 }
1371 break;
1372 default:
1373 LOG(FATAL) << "unknown tag " << tag;
1374 break;
1375 }
1376
1377 // Prepend tag, which may have been updated.
1378 JDWP::Set1(buf, tag);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001379}
1380
Elliott Hughesdbb40792011-11-18 17:05:22 -08001381void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width) {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001382 Method** sp = reinterpret_cast<Method**>(frameId);
1383 Frame f;
1384 f.SetSP(sp);
1385 uint16_t reg = DemangleSlot(slot, f);
1386 Method* m = f.GetMethod();
1387
1388 const VmapTable vmap_table(m->GetVmapTableRaw());
1389 uint32_t vmap_offset;
1390 if (vmap_table.IsInContext(reg, vmap_offset)) {
1391 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1392 }
1393
1394 switch (tag) {
1395 case JDWP::JT_BOOLEAN:
1396 case JDWP::JT_BYTE:
1397 CHECK_EQ(width, 1U);
1398 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1399 break;
1400 case JDWP::JT_SHORT:
1401 case JDWP::JT_CHAR:
1402 CHECK_EQ(width, 2U);
1403 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1404 break;
1405 case JDWP::JT_INT:
1406 case JDWP::JT_FLOAT:
1407 CHECK_EQ(width, 4U);
1408 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1409 break;
1410 case JDWP::JT_ARRAY:
1411 case JDWP::JT_OBJECT:
1412 case JDWP::JT_STRING:
1413 {
1414 CHECK_EQ(width, sizeof(JDWP::ObjectId));
1415 Object* o = gRegistry->Get<Object*>(static_cast<JDWP::ObjectId>(value));
1416 f.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)));
1417 }
1418 break;
1419 case JDWP::JT_DOUBLE:
1420 case JDWP::JT_LONG:
1421 CHECK_EQ(width, 8U);
1422 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1423 f.SetVReg(m, reg + 1, static_cast<uint32_t>(value >> 32));
1424 break;
1425 default:
1426 LOG(FATAL) << "unknown tag " << tag;
1427 break;
1428 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001429}
1430
1431void Dbg::PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags) {
1432 UNIMPLEMENTED(FATAL);
1433}
1434
Elliott Hughesd07986f2011-12-06 18:27:45 -08001435void Dbg::PostException(Method** sp, Method* throwMethod, uintptr_t throwNativePc, Method* catchMethod, uintptr_t catchNativePc, Object* exception) {
Ian Rogers0ad5bb82011-12-07 10:16:32 -08001436 if (!gDebuggerActive) {
1437 return;
1438 }
Elliott Hughesd07986f2011-12-06 18:27:45 -08001439 JDWP::JdwpLocation throw_location;
1440 SetLocation(throw_location, throwMethod, throwNativePc);
1441 JDWP::JdwpLocation catch_location;
1442 SetLocation(catch_location, catchMethod, catchNativePc);
1443
1444 // We need 'this' for InstanceOnly filters.
1445 JDWP::ObjectId this_id;
1446 GetThisObject(reinterpret_cast<JDWP::FrameId>(sp), &this_id);
1447
1448 /*
1449 * Hand the event to the JDWP exception handler. Note we're using the
1450 * "NoReg" objectID on the exception, which is not strictly correct --
1451 * the exception object WILL be passed up to the debugger if the
1452 * debugger is interested in the event. We do this because the current
1453 * implementation of the debugger object registry never throws anything
1454 * away, and some people were experiencing a fatal build up of exception
1455 * objects when dealing with certain libraries.
1456 */
1457 JDWP::ObjectId exception_id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(exception));
1458 JDWP::RefTypeId exception_class_id = gRegistry->Add(exception->GetClass());
1459
1460 gJdwpState->PostException(&throw_location, exception_id, exception_class_id, &catch_location, this_id);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001461}
1462
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001463void Dbg::PostClassPrepare(Class* c) {
1464 UNIMPLEMENTED(FATAL);
1465}
1466
1467bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
1468 UNIMPLEMENTED(FATAL);
1469 return false;
1470}
1471
1472void Dbg::UnwatchLocation(const JDWP::JdwpLocation* pLoc) {
1473 UNIMPLEMENTED(FATAL);
1474}
1475
1476bool Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) {
1477 UNIMPLEMENTED(FATAL);
1478 return false;
1479}
1480
1481void Dbg::UnconfigureStep(JDWP::ObjectId threadId) {
1482 UNIMPLEMENTED(FATAL);
1483}
1484
Elliott Hughesd07986f2011-12-06 18:27:45 -08001485JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptionId) {
1486 ThreadList* thread_list = Runtime::Current()->GetThreadList();
1487
1488 Thread* targetThread = NULL;
1489 DebugInvokeReq* req = NULL;
1490 {
1491 ScopedThreadListLock thread_list_lock;
1492 targetThread = DecodeThread(threadId);
1493 if (targetThread == NULL) {
1494 LOG(ERROR) << "InvokeMethod request for non-existent thread " << threadId;
1495 return JDWP::ERR_INVALID_THREAD;
1496 }
1497 req = targetThread->GetInvokeReq();
1498 if (!req->ready) {
1499 LOG(ERROR) << "InvokeMethod request for thread not stopped by event: " << *targetThread;
1500 return JDWP::ERR_INVALID_THREAD;
1501 }
1502
1503 /*
1504 * We currently have a bug where we don't successfully resume the
1505 * target thread if the suspend count is too deep. We're expected to
1506 * require one "resume" for each "suspend", but when asked to execute
1507 * a method we have to resume fully and then re-suspend it back to the
1508 * same level. (The easiest way to cause this is to type "suspend"
1509 * multiple times in jdb.)
1510 *
1511 * It's unclear what this means when the event specifies "resume all"
1512 * and some threads are suspended more deeply than others. This is
1513 * a rare problem, so for now we just prevent it from hanging forever
1514 * by rejecting the method invocation request. Without this, we will
1515 * be stuck waiting on a suspended thread.
1516 */
1517 int suspend_count = targetThread->GetSuspendCount();
1518 if (suspend_count > 1) {
1519 LOG(ERROR) << *targetThread << " suspend count too deep for method invocation: " << suspend_count;
1520 return JDWP::ERR_THREAD_SUSPENDED; // Probably not expected here.
1521 }
1522
1523 /*
1524 * TODO: ought to screen the various IDs, and verify that the argument
1525 * list is valid.
1526 */
1527 req->receiver_ = gRegistry->Get<Object*>(objectId);
1528 req->thread_ = gRegistry->Get<Object*>(threadId);
1529 req->class_ = gRegistry->Get<Class*>(classId);
1530 req->method_ = FromMethodId(methodId);
1531 req->num_args_ = numArgs;
1532 req->arg_array_ = argArray;
1533 req->options_ = options;
1534 req->invoke_needed_ = true;
1535 }
1536
1537 // The fact that we've released the thread list lock is a bit risky --- if the thread goes
1538 // away we're sitting high and dry -- but we must release this before the ResumeAllThreads
1539 // call, and it's unwise to hold it during WaitForSuspend.
1540
1541 {
1542 /*
1543 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
1544 * so the VM can suspend for a GC if the invoke request causes us to
1545 * run out of memory. It's also a good idea to change it before locking
1546 * the invokeReq mutex, although that should never be held for long.
1547 */
1548 ScopedThreadStateChange tsc(Thread::Current(), Thread::kVmWait);
1549
1550 LOG(VERBOSE) << " Transferring control to event thread";
1551 {
1552 MutexLock mu(req->lock_);
1553
1554 if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
1555 LOG(VERBOSE) << " Resuming all threads";
1556 thread_list->ResumeAll(true);
1557 } else {
1558 LOG(VERBOSE) << " Resuming event thread only";
1559 thread_list->Resume(targetThread, true);
1560 }
1561
1562 // Wait for the request to finish executing.
1563 while (req->invoke_needed_) {
1564 req->cond_.Wait(req->lock_);
1565 }
1566 }
1567 LOG(VERBOSE) << " Control has returned from event thread";
1568
1569 /* wait for thread to re-suspend itself */
1570 targetThread->WaitUntilSuspended();
1571 //dvmWaitForSuspend(targetThread);
1572 }
1573
1574 /*
1575 * Suspend the threads. We waited for the target thread to suspend
1576 * itself, so all we need to do is suspend the others.
1577 *
1578 * The suspendAllThreads() call will double-suspend the event thread,
1579 * so we want to resume the target thread once to keep the books straight.
1580 */
1581 if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
1582 LOG(VERBOSE) << " Suspending all threads";
1583 thread_list->SuspendAll(true);
1584 LOG(VERBOSE) << " Resuming event thread to balance the count";
1585 thread_list->Resume(targetThread, true);
1586 }
1587
1588 // Copy the result.
1589 *pResultTag = req->result_tag;
1590 if (IsPrimitiveTag(req->result_tag)) {
1591 *pResultValue = req->result_value.j;
1592 } else {
1593 *pResultValue = gRegistry->Add(req->result_value.l);
1594 }
1595 *pExceptionId = req->exception;
1596 return req->error;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001597}
1598
1599void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
Elliott Hughesd07986f2011-12-06 18:27:45 -08001600 Thread* self = Thread::Current();
1601
1602 // We can be called while an exception is pending in the VM. We need
1603 // to preserve that across the method invocation.
1604 SirtRef<Throwable> old_exception(self->GetException());
1605 self->ClearException();
1606
1607 ScopedThreadStateChange tsc(self, Thread::kRunnable);
1608
1609 // Translate the method through the vtable, unless the debugger wants to suppress it.
1610 Method* m = pReq->method_;
1611 if ((pReq->options_ & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver_ != NULL) {
1612 m = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_);
1613 }
1614 CHECK(m != NULL);
1615
1616 CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
1617
1618 pReq->result_value = InvokeWithJValues(self, pReq->receiver_, m, reinterpret_cast<JValue*>(pReq->arg_array_));
1619
1620 pReq->exception = gRegistry->Add(self->GetException());
1621 pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty());
1622 if (pReq->exception != 0) {
1623 Object* exc = self->GetException();
1624 LOG(VERBOSE) << " JDWP invocation returning with exception=" << exc << " " << PrettyTypeOf(exc);
1625 self->ClearException();
1626 pReq->result_value.j = 0;
1627 } else if (pReq->result_tag == JDWP::JT_OBJECT) {
1628 /* if no exception thrown, examine object result more closely */
1629 JDWP::JdwpTag new_tag = TagFromObject(pReq->result_value.l);
1630 if (new_tag != pReq->result_tag) {
1631 LOG(VERBOSE) << " JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
1632 pReq->result_tag = new_tag;
1633 }
1634
1635 /*
1636 * Register the object. We don't actually need an ObjectId yet,
1637 * but we do need to be sure that the GC won't move or discard the
1638 * object when we switch out of RUNNING. The ObjectId conversion
1639 * will add the object to the "do not touch" list.
1640 *
1641 * We can't use the "tracked allocation" mechanism here because
1642 * the object is going to be handed off to a different thread.
1643 */
1644 gRegistry->Add(pReq->result_value.l);
1645 }
1646
1647 if (old_exception.get() != NULL) {
1648 self->SetException(old_exception.get());
1649 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001650}
1651
Elliott Hughesd07986f2011-12-06 18:27:45 -08001652/*
1653 * Register an object ID that might not have been registered previously.
1654 *
1655 * Normally this wouldn't happen -- the conversion to an ObjectId would
1656 * have added the object to the registry -- but in some cases (e.g.
1657 * throwing exceptions) we really want to do the registration late.
1658 */
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001659void Dbg::RegisterObjectId(JDWP::ObjectId id) {
Elliott Hughesd07986f2011-12-06 18:27:45 -08001660 gRegistry->Add(reinterpret_cast<Object*>(id));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001661}
1662
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001663/*
1664 * "buf" contains a full JDWP packet, possibly with multiple chunks. We
1665 * need to process each, accumulate the replies, and ship the whole thing
1666 * back.
1667 *
1668 * Returns "true" if we have a reply. The reply buffer is newly allocated,
1669 * and includes the chunk type/length, followed by the data.
1670 *
1671 * TODO: we currently assume that the request and reply include a single
1672 * chunk. If this becomes inconvenient we will need to adapt.
1673 */
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001674bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001675 CHECK_GE(dataLen, 0);
1676
1677 Thread* self = Thread::Current();
1678 JNIEnv* env = self->GetJniEnv();
1679
1680 static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
1681 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1682 static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
1683 "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
1684 static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
1685 static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
1686 static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
1687 static jfieldID type_fid = env->GetFieldID(Chunk_class, "type", "I");
1688
1689 // Create a byte[] corresponding to 'buf'.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001690 ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(dataLen));
1691 if (dataArray.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001692 LOG(WARNING) << "byte[] allocation failed: " << dataLen;
1693 env->ExceptionClear();
1694 return false;
1695 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001696 env->SetByteArrayRegion(dataArray.get(), 0, dataLen, reinterpret_cast<const jbyte*>(buf));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001697
1698 const int kChunkHdrLen = 8;
1699
1700 // Run through and find all chunks. [Currently just find the first.]
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001701 ScopedByteArrayRO contents(env, dataArray.get());
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001702 jint type = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[0]));
1703 jint length = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[4]));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001704 jint offset = kChunkHdrLen;
1705 if (offset + length > dataLen) {
1706 LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%d)", length, dataLen);
1707 return false;
1708 }
1709
1710 // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001711 ScopedLocalRef<jobject> chunk(env, env->CallStaticObjectMethod(DdmServer_class, dispatch_mid, type, dataArray.get(), offset, length));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001712 if (env->ExceptionCheck()) {
1713 LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
1714 env->ExceptionDescribe();
1715 env->ExceptionClear();
1716 return false;
1717 }
1718
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001719 if (chunk.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001720 return false;
1721 }
1722
1723 /*
1724 * Pull the pieces out of the chunk. We copy the results into a
1725 * newly-allocated buffer that the caller can free. We don't want to
1726 * continue using the Chunk object because nothing has a reference to it.
1727 *
1728 * We could avoid this by returning type/data/offset/length and having
1729 * the caller be aware of the object lifetime issues, but that
1730 * integrates the JDWP code more tightly into the VM, and doesn't work
1731 * if we have responses for multiple chunks.
1732 *
1733 * So we're pretty much stuck with copying data around multiple times.
1734 */
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001735 ScopedLocalRef<jbyteArray> replyData(env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk.get(), data_fid)));
1736 length = env->GetIntField(chunk.get(), length_fid);
1737 offset = env->GetIntField(chunk.get(), offset_fid);
1738 type = env->GetIntField(chunk.get(), type_fid);
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001739
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001740 LOG(VERBOSE) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
1741 if (length == 0 || replyData.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001742 return false;
1743 }
1744
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001745 jsize replyLength = env->GetArrayLength(replyData.get());
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001746 if (offset + length > replyLength) {
1747 LOG(WARNING) << StringPrintf("chunk off=%d len=%d exceeds reply array len %d", offset, length, replyLength);
1748 return false;
1749 }
1750
1751 uint8_t* reply = new uint8_t[length + kChunkHdrLen];
1752 if (reply == NULL) {
1753 LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
1754 return false;
1755 }
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001756 JDWP::Set4BE(reply + 0, type);
1757 JDWP::Set4BE(reply + 4, length);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001758 env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001759
1760 *pReplyBuf = reply;
1761 *pReplyLen = length + kChunkHdrLen;
1762
1763 LOG(VERBOSE) << StringPrintf("dvmHandleDdm returning type=%.4s buf=%p len=%d", (char*) reply, reply, length);
1764 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001765}
1766
Elliott Hughesa2155262011-11-16 16:26:58 -08001767void Dbg::DdmBroadcast(bool connect) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001768 LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
1769
1770 Thread* self = Thread::Current();
1771 if (self->GetState() != Thread::kRunnable) {
1772 LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
1773 /* try anyway? */
1774 }
1775
1776 JNIEnv* env = self->GetJniEnv();
1777 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1778 static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
1779 jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
1780 env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
1781 if (env->ExceptionCheck()) {
1782 LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
1783 env->ExceptionDescribe();
1784 env->ExceptionClear();
1785 }
1786}
1787
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001788void Dbg::DdmConnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001789 Dbg::DdmBroadcast(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001790}
1791
1792void Dbg::DdmDisconnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001793 Dbg::DdmBroadcast(false);
Elliott Hughes47fce012011-10-25 18:37:19 -07001794 gDdmThreadNotification = false;
1795}
1796
1797/*
Elliott Hughes82188472011-11-07 18:11:48 -08001798 * Send a notification when a thread starts, stops, or changes its name.
Elliott Hughes47fce012011-10-25 18:37:19 -07001799 *
1800 * Because we broadcast the full set of threads when the notifications are
1801 * first enabled, it's possible for "thread" to be actively executing.
1802 */
Elliott Hughes82188472011-11-07 18:11:48 -08001803void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001804 if (!gDdmThreadNotification) {
1805 return;
1806 }
1807
Elliott Hughes82188472011-11-07 18:11:48 -08001808 if (type == CHUNK_TYPE("THDE")) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001809 uint8_t buf[4];
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001810 JDWP::Set4BE(&buf[0], t->GetThinLockId());
Elliott Hughes47fce012011-10-25 18:37:19 -07001811 Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
Elliott Hughes82188472011-11-07 18:11:48 -08001812 } else {
1813 CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
1814 SirtRef<String> name(t->GetName());
1815 size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
1816 const jchar* chars = name->GetCharArray()->GetData();
1817
Elliott Hughes21f32d72011-11-09 17:44:13 -08001818 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001819 JDWP::Append4BE(bytes, t->GetThinLockId());
1820 JDWP::AppendUtf16BE(bytes, chars, char_count);
Elliott Hughes21f32d72011-11-09 17:44:13 -08001821 CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
1822 Dbg::DdmSendChunk(type, bytes);
Elliott Hughes47fce012011-10-25 18:37:19 -07001823 }
1824}
1825
Elliott Hughesa2155262011-11-16 16:26:58 -08001826static void DdmSendThreadStartCallback(Thread* t, void*) {
Elliott Hughes82188472011-11-07 18:11:48 -08001827 Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001828}
1829
1830void Dbg::DdmSetThreadNotification(bool enable) {
1831 // We lock the thread list to avoid sending duplicate events or missing
1832 // a thread change. We should be okay holding this lock while sending
1833 // the messages out. (We have to hold it while accessing a live thread.)
Elliott Hughesbbd9d832011-11-07 14:40:00 -08001834 ScopedThreadListLock thread_list_lock;
Elliott Hughes47fce012011-10-25 18:37:19 -07001835
1836 gDdmThreadNotification = enable;
1837 if (enable) {
Elliott Hughesbfe487b2011-10-26 15:48:55 -07001838 Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback, NULL);
Elliott Hughes47fce012011-10-25 18:37:19 -07001839 }
1840}
1841
Elliott Hughesa2155262011-11-16 16:26:58 -08001842void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001843 if (gDebuggerActive) {
1844 JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
Elliott Hughes82188472011-11-07 18:11:48 -08001845 gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001846 }
Elliott Hughes82188472011-11-07 18:11:48 -08001847 Dbg::DdmSendThreadNotification(t, type);
Elliott Hughes47fce012011-10-25 18:37:19 -07001848}
1849
1850void Dbg::PostThreadStart(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001851 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001852}
1853
1854void Dbg::PostThreadDeath(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001855 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001856}
1857
Elliott Hughes82188472011-11-07 18:11:48 -08001858void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001859 CHECK(buf != NULL);
1860 iovec vec[1];
1861 vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
1862 vec[0].iov_len = byte_count;
1863 Dbg::DdmSendChunkV(type, vec, 1);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001864}
1865
Elliott Hughes21f32d72011-11-09 17:44:13 -08001866void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
1867 DdmSendChunk(type, bytes.size(), &bytes[0]);
1868}
1869
Elliott Hughescccd84f2011-12-05 16:51:54 -08001870void Dbg::DdmSendChunkV(uint32_t type, const struct iovec* iov, int iov_count) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001871 if (gJdwpState == NULL) {
1872 LOG(VERBOSE) << "Debugger thread not active, ignoring DDM send: " << type;
1873 } else {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001874 gJdwpState->DdmSendChunkV(type, iov, iov_count);
Elliott Hughes3bb81562011-10-21 18:52:59 -07001875 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001876}
1877
Elliott Hughes767a1472011-10-26 18:49:02 -07001878int Dbg::DdmHandleHpifChunk(HpifWhen when) {
1879 if (when == HPIF_WHEN_NOW) {
Elliott Hughes7162ad92011-10-27 14:08:42 -07001880 DdmSendHeapInfo(when);
Elliott Hughes767a1472011-10-26 18:49:02 -07001881 return true;
1882 }
1883
1884 if (when != HPIF_WHEN_NEVER && when != HPIF_WHEN_NEXT_GC && when != HPIF_WHEN_EVERY_GC) {
1885 LOG(ERROR) << "invalid HpifWhen value: " << static_cast<int>(when);
1886 return false;
1887 }
1888
1889 gDdmHpifWhen = when;
1890 return true;
1891}
1892
1893bool Dbg::DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when, Dbg::HpsgWhat what, bool native) {
1894 if (when != HPSG_WHEN_NEVER && when != HPSG_WHEN_EVERY_GC) {
1895 LOG(ERROR) << "invalid HpsgWhen value: " << static_cast<int>(when);
1896 return false;
1897 }
1898
1899 if (what != HPSG_WHAT_MERGED_OBJECTS && what != HPSG_WHAT_DISTINCT_OBJECTS) {
1900 LOG(ERROR) << "invalid HpsgWhat value: " << static_cast<int>(what);
1901 return false;
1902 }
1903
1904 if (native) {
1905 gDdmNhsgWhen = when;
1906 gDdmNhsgWhat = what;
1907 } else {
1908 gDdmHpsgWhen = when;
1909 gDdmHpsgWhat = what;
1910 }
1911 return true;
1912}
1913
Elliott Hughes7162ad92011-10-27 14:08:42 -07001914void Dbg::DdmSendHeapInfo(HpifWhen reason) {
1915 // If there's a one-shot 'when', reset it.
1916 if (reason == gDdmHpifWhen) {
1917 if (gDdmHpifWhen == HPIF_WHEN_NEXT_GC) {
1918 gDdmHpifWhen = HPIF_WHEN_NEVER;
1919 }
1920 }
1921
1922 /*
1923 * Chunk HPIF (client --> server)
1924 *
1925 * Heap Info. General information about the heap,
1926 * suitable for a summary display.
1927 *
1928 * [u4]: number of heaps
1929 *
1930 * For each heap:
1931 * [u4]: heap ID
1932 * [u8]: timestamp in ms since Unix epoch
1933 * [u1]: capture reason (same as 'when' value from server)
1934 * [u4]: max heap size in bytes (-Xmx)
1935 * [u4]: current heap size in bytes
1936 * [u4]: current number of bytes allocated
1937 * [u4]: current number of objects allocated
1938 */
1939 uint8_t heap_count = 1;
Elliott Hughes21f32d72011-11-09 17:44:13 -08001940 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001941 JDWP::Append4BE(bytes, heap_count);
1942 JDWP::Append4BE(bytes, 1); // Heap id (bogus; we only have one heap).
1943 JDWP::Append8BE(bytes, MilliTime());
1944 JDWP::Append1BE(bytes, reason);
1945 JDWP::Append4BE(bytes, Heap::GetMaxMemory()); // Max allowed heap size in bytes.
1946 JDWP::Append4BE(bytes, Heap::GetTotalMemory()); // Current heap size in bytes.
1947 JDWP::Append4BE(bytes, Heap::GetBytesAllocated());
1948 JDWP::Append4BE(bytes, Heap::GetObjectsAllocated());
Elliott Hughes21f32d72011-11-09 17:44:13 -08001949 CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
1950 Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
Elliott Hughes767a1472011-10-26 18:49:02 -07001951}
1952
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001953enum HpsgSolidity {
1954 SOLIDITY_FREE = 0,
1955 SOLIDITY_HARD = 1,
1956 SOLIDITY_SOFT = 2,
1957 SOLIDITY_WEAK = 3,
1958 SOLIDITY_PHANTOM = 4,
1959 SOLIDITY_FINALIZABLE = 5,
1960 SOLIDITY_SWEEP = 6,
1961};
1962
1963enum HpsgKind {
1964 KIND_OBJECT = 0,
1965 KIND_CLASS_OBJECT = 1,
1966 KIND_ARRAY_1 = 2,
1967 KIND_ARRAY_2 = 3,
1968 KIND_ARRAY_4 = 4,
1969 KIND_ARRAY_8 = 5,
1970 KIND_UNKNOWN = 6,
1971 KIND_NATIVE = 7,
1972};
1973
1974#define HPSG_PARTIAL (1<<7)
1975#define HPSG_STATE(solidity, kind) ((uint8_t)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
1976
1977struct HeapChunkContext {
1978 std::vector<uint8_t> buf;
1979 uint8_t* p;
1980 uint8_t* pieceLenField;
1981 size_t totalAllocationUnits;
Elliott Hughes82188472011-11-07 18:11:48 -08001982 uint32_t type;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001983 bool merge;
1984 bool needHeader;
1985
1986 // Maximum chunk size. Obtain this from the formula:
1987 // (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
1988 HeapChunkContext(bool merge, bool native)
1989 : buf(16384 - 16),
1990 type(0),
1991 merge(merge) {
1992 Reset();
1993 if (native) {
1994 type = CHUNK_TYPE("NHSG");
1995 } else {
1996 type = merge ? CHUNK_TYPE("HPSG") : CHUNK_TYPE("HPSO");
1997 }
1998 }
1999
2000 ~HeapChunkContext() {
2001 if (p > &buf[0]) {
2002 Flush();
2003 }
2004 }
2005
2006 void EnsureHeader(const void* chunk_ptr) {
2007 if (!needHeader) {
2008 return;
2009 }
2010
2011 // Start a new HPSx chunk.
2012 JDWP::Write4BE(&p, 1); // Heap id (bogus; we only have one heap).
2013 JDWP::Write1BE(&p, 8); // Size of allocation unit, in bytes.
2014
2015 JDWP::Write4BE(&p, reinterpret_cast<uintptr_t>(chunk_ptr)); // virtual address of segment start.
2016 JDWP::Write4BE(&p, 0); // offset of this piece (relative to the virtual address).
2017 // [u4]: length of piece, in allocation units
2018 // We won't know this until we're done, so save the offset and stuff in a dummy value.
2019 pieceLenField = p;
2020 JDWP::Write4BE(&p, 0x55555555);
2021 needHeader = false;
2022 }
2023
2024 void Flush() {
2025 // Patch the "length of piece" field.
2026 CHECK_LE(&buf[0], pieceLenField);
2027 CHECK_LE(pieceLenField, p);
2028 JDWP::Set4BE(pieceLenField, totalAllocationUnits);
2029
2030 Dbg::DdmSendChunk(type, p - &buf[0], &buf[0]);
2031 Reset();
2032 }
2033
Elliott Hughesa2155262011-11-16 16:26:58 -08002034 static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
2035 reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
2036 }
2037
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002038 private:
Elliott Hughesa2155262011-11-16 16:26:58 -08002039 enum { ALLOCATION_UNIT_SIZE = 8 };
2040
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002041 void Reset() {
2042 p = &buf[0];
2043 totalAllocationUnits = 0;
2044 needHeader = true;
2045 pieceLenField = NULL;
2046 }
2047
Elliott Hughesa2155262011-11-16 16:26:58 -08002048 void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
2049 CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002050
Elliott Hughesa2155262011-11-16 16:26:58 -08002051 /* Make sure there's enough room left in the buffer.
2052 * We need to use two bytes for every fractional 256
2053 * allocation units used by the chunk.
2054 */
2055 {
2056 size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
2057 size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
2058 if (bytesLeft < needed) {
2059 Flush();
2060 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002061
Elliott Hughesa2155262011-11-16 16:26:58 -08002062 bytesLeft = buf.size() - (size_t)(p - &buf[0]);
2063 if (bytesLeft < needed) {
2064 LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
2065 return;
2066 }
2067 }
2068
2069 // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
2070 EnsureHeader(chunk_ptr);
2071
2072 // Determine the type of this chunk.
2073 // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
2074 // If it's the same, we should combine them.
2075 uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
2076
2077 // Write out the chunk description.
2078 chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
2079 totalAllocationUnits += chunk_len;
2080 while (chunk_len > 256) {
2081 *p++ = state | HPSG_PARTIAL;
2082 *p++ = 255; // length - 1
2083 chunk_len -= 256;
2084 }
2085 *p++ = state;
2086 *p++ = chunk_len - 1;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002087 }
2088
Elliott Hughesa2155262011-11-16 16:26:58 -08002089 uint8_t ExamineObject(const Object* o, bool is_native_heap) {
2090 if (o == NULL) {
2091 return HPSG_STATE(SOLIDITY_FREE, 0);
2092 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002093
Elliott Hughesa2155262011-11-16 16:26:58 -08002094 // It's an allocated chunk. Figure out what it is.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002095
Elliott Hughesa2155262011-11-16 16:26:58 -08002096 // If we're looking at the native heap, we'll just return
2097 // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
2098 if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
2099 return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
2100 }
2101
2102 Class* c = o->GetClass();
2103 if (c == NULL) {
2104 // The object was probably just created but hasn't been initialized yet.
2105 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
2106 }
2107
2108 if (!Heap::IsHeapAddress(c)) {
2109 LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
2110 return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
2111 }
2112
2113 if (c->IsClassClass()) {
2114 return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
2115 }
2116
2117 if (c->IsArrayClass()) {
2118 if (o->IsObjectArray()) {
2119 return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
2120 }
2121 switch (c->GetComponentSize()) {
2122 case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
2123 case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
2124 case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
2125 case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
2126 }
2127 }
2128
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002129 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
2130 }
2131
Elliott Hughesa2155262011-11-16 16:26:58 -08002132 DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
2133};
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002134
2135void Dbg::DdmSendHeapSegments(bool native) {
2136 Dbg::HpsgWhen when;
2137 Dbg::HpsgWhat what;
2138 if (!native) {
2139 when = gDdmHpsgWhen;
2140 what = gDdmHpsgWhat;
2141 } else {
2142 when = gDdmNhsgWhen;
2143 what = gDdmNhsgWhat;
2144 }
2145 if (when == HPSG_WHEN_NEVER) {
2146 return;
2147 }
2148
2149 // Figure out what kind of chunks we'll be sending.
2150 CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
2151
2152 // First, send a heap start chunk.
2153 uint8_t heap_id[4];
2154 JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
2155 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
2156
2157 // Send a series of heap segment chunks.
Elliott Hughesa2155262011-11-16 16:26:58 -08002158 HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
2159 if (native) {
2160 dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
2161 } else {
2162 Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
2163 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07002164
2165 // Finally, send a heap end chunk.
2166 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
Elliott Hughes767a1472011-10-26 18:49:02 -07002167}
2168
Elliott Hughes545a0642011-11-08 19:10:03 -08002169void Dbg::SetAllocTrackingEnabled(bool enabled) {
2170 MutexLock mu(gAllocTrackerLock);
2171 if (enabled) {
2172 if (recent_allocation_records_ == NULL) {
2173 LOG(INFO) << "Enabling alloc tracker (" << kNumAllocRecords << " entries, "
2174 << kMaxAllocRecordStackDepth << " frames --> "
2175 << (sizeof(AllocRecord) * kNumAllocRecords) << " bytes)";
2176 gAllocRecordHead = gAllocRecordCount = 0;
2177 recent_allocation_records_ = new AllocRecord[kNumAllocRecords];
2178 CHECK(recent_allocation_records_ != NULL);
2179 }
2180 } else {
2181 delete[] recent_allocation_records_;
2182 recent_allocation_records_ = NULL;
2183 }
2184}
2185
2186struct AllocRecordStackVisitor : public Thread::StackVisitor {
2187 AllocRecordStackVisitor(AllocRecord* record) : record(record), depth(0) {
2188 }
2189
2190 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
2191 if (depth >= kMaxAllocRecordStackDepth) {
2192 return;
2193 }
2194 Method* m = f.GetMethod();
2195 if (m == NULL || m->IsCalleeSaveMethod()) {
2196 return;
2197 }
2198 record->stack[depth].method = m;
2199 record->stack[depth].raw_pc = pc;
2200 ++depth;
2201 }
2202
2203 ~AllocRecordStackVisitor() {
2204 // Clear out any unused stack trace elements.
2205 for (; depth < kMaxAllocRecordStackDepth; ++depth) {
2206 record->stack[depth].method = NULL;
2207 record->stack[depth].raw_pc = 0;
2208 }
2209 }
2210
2211 AllocRecord* record;
2212 size_t depth;
2213};
2214
2215void Dbg::RecordAllocation(Class* type, size_t byte_count) {
2216 Thread* self = Thread::Current();
2217 CHECK(self != NULL);
2218
2219 MutexLock mu(gAllocTrackerLock);
2220 if (recent_allocation_records_ == NULL) {
2221 return;
2222 }
2223
2224 // Advance and clip.
2225 if (++gAllocRecordHead == kNumAllocRecords) {
2226 gAllocRecordHead = 0;
2227 }
2228
2229 // Fill in the basics.
2230 AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
2231 record->type = type;
2232 record->byte_count = byte_count;
2233 record->thin_lock_id = self->GetThinLockId();
2234
2235 // Fill in the stack trace.
2236 AllocRecordStackVisitor visitor(record);
2237 self->WalkStack(&visitor);
2238
2239 if (gAllocRecordCount < kNumAllocRecords) {
2240 ++gAllocRecordCount;
2241 }
2242}
2243
2244/*
2245 * Return the index of the head element.
2246 *
2247 * We point at the most-recently-written record, so if allocRecordCount is 1
2248 * we want to use the current element. Take "head+1" and subtract count
2249 * from it.
2250 *
2251 * We need to handle underflow in our circular buffer, so we add
2252 * kNumAllocRecords and then mask it back down.
2253 */
2254inline static int headIndex() {
2255 return (gAllocRecordHead+1 + kNumAllocRecords - gAllocRecordCount) & (kNumAllocRecords-1);
2256}
2257
2258void Dbg::DumpRecentAllocations() {
2259 MutexLock mu(gAllocTrackerLock);
2260 if (recent_allocation_records_ == NULL) {
2261 LOG(INFO) << "Not recording tracked allocations";
2262 return;
2263 }
2264
2265 // "i" is the head of the list. We want to start at the end of the
2266 // list and move forward to the tail.
2267 size_t i = headIndex();
2268 size_t count = gAllocRecordCount;
2269
2270 LOG(INFO) << "Tracked allocations, (head=" << gAllocRecordHead << " count=" << count << ")";
2271 while (count--) {
2272 AllocRecord* record = &recent_allocation_records_[i];
2273
2274 LOG(INFO) << StringPrintf(" T=%-2d %6d ", record->thin_lock_id, record->byte_count)
2275 << PrettyClass(record->type);
2276
2277 for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
2278 const Method* m = record->stack[stack_frame].method;
2279 if (m == NULL) {
2280 break;
2281 }
2282 LOG(INFO) << " " << PrettyMethod(m) << " line " << record->stack[stack_frame].LineNumber();
2283 }
2284
2285 // pause periodically to help logcat catch up
2286 if ((count % 5) == 0) {
2287 usleep(40000);
2288 }
2289
2290 i = (i + 1) & (kNumAllocRecords-1);
2291 }
2292}
2293
2294class StringTable {
2295 public:
2296 StringTable() {
2297 }
2298
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002299 void Add(const char* s) {
Elliott Hughes545a0642011-11-08 19:10:03 -08002300 table_.insert(s);
2301 }
2302
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002303 size_t IndexOf(const char* s) {
Elliott Hughes545a0642011-11-08 19:10:03 -08002304 return std::distance(table_.begin(), table_.find(s));
2305 }
2306
2307 size_t Size() {
2308 return table_.size();
2309 }
2310
2311 void WriteTo(std::vector<uint8_t>& bytes) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002312 typedef std::set<const char*>::const_iterator It; // TODO: C++0x auto
Elliott Hughes545a0642011-11-08 19:10:03 -08002313 for (It it = table_.begin(); it != table_.end(); ++it) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002314 const char* s = *it;
2315 size_t s_len = CountModifiedUtf8Chars(s);
2316 UniquePtr<uint16_t> s_utf16(new uint16_t[s_len]);
2317 ConvertModifiedUtf8ToUtf16(s_utf16.get(), s);
2318 JDWP::AppendUtf16BE(bytes, s_utf16.get(), s_len);
Elliott Hughes545a0642011-11-08 19:10:03 -08002319 }
2320 }
2321
2322 private:
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002323 std::set<const char*> table_;
Elliott Hughes545a0642011-11-08 19:10:03 -08002324 DISALLOW_COPY_AND_ASSIGN(StringTable);
2325};
2326
2327/*
2328 * The data we send to DDMS contains everything we have recorded.
2329 *
2330 * Message header (all values big-endian):
2331 * (1b) message header len (to allow future expansion); includes itself
2332 * (1b) entry header len
2333 * (1b) stack frame len
2334 * (2b) number of entries
2335 * (4b) offset to string table from start of message
2336 * (2b) number of class name strings
2337 * (2b) number of method name strings
2338 * (2b) number of source file name strings
2339 * For each entry:
2340 * (4b) total allocation size
2341 * (2b) threadId
2342 * (2b) allocated object's class name index
2343 * (1b) stack depth
2344 * For each stack frame:
2345 * (2b) method's class name
2346 * (2b) method name
2347 * (2b) method source file
2348 * (2b) line number, clipped to 32767; -2 if native; -1 if no source
2349 * (xb) class name strings
2350 * (xb) method name strings
2351 * (xb) source file strings
2352 *
2353 * As with other DDM traffic, strings are sent as a 4-byte length
2354 * followed by UTF-16 data.
2355 *
2356 * We send up 16-bit unsigned indexes into string tables. In theory there
2357 * can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
2358 * each table, but in practice there should be far fewer.
2359 *
2360 * The chief reason for using a string table here is to keep the size of
2361 * the DDMS message to a minimum. This is partly to make the protocol
2362 * efficient, but also because we have to form the whole thing up all at
2363 * once in a memory buffer.
2364 *
2365 * We use separate string tables for class names, method names, and source
2366 * files to keep the indexes small. There will generally be no overlap
2367 * between the contents of these tables.
2368 */
2369jbyteArray Dbg::GetRecentAllocations() {
2370 if (false) {
2371 DumpRecentAllocations();
2372 }
2373
2374 MutexLock mu(gAllocTrackerLock);
2375
2376 /*
2377 * Part 1: generate string tables.
2378 */
2379 StringTable class_names;
2380 StringTable method_names;
2381 StringTable filenames;
2382
2383 int count = gAllocRecordCount;
2384 int idx = headIndex();
2385 while (count--) {
2386 AllocRecord* record = &recent_allocation_records_[idx];
2387
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002388 class_names.Add(ClassHelper(record->type).GetDescriptor().c_str());
Elliott Hughes545a0642011-11-08 19:10:03 -08002389
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002390 MethodHelper mh;
Elliott Hughes545a0642011-11-08 19:10:03 -08002391 for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002392 Method* m = record->stack[i].method;
2393 mh.ChangeMethod(m);
Elliott Hughes545a0642011-11-08 19:10:03 -08002394 if (m != NULL) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002395 class_names.Add(mh.GetDeclaringClassDescriptor());
2396 method_names.Add(mh.GetName());
2397 filenames.Add(mh.GetDeclaringClassSourceFile());
Elliott Hughes545a0642011-11-08 19:10:03 -08002398 }
2399 }
2400
2401 idx = (idx + 1) & (kNumAllocRecords-1);
2402 }
2403
2404 LOG(INFO) << "allocation records: " << gAllocRecordCount;
2405
2406 /*
2407 * Part 2: allocate a buffer and generate the output.
2408 */
2409 std::vector<uint8_t> bytes;
2410
2411 // (1b) message header len (to allow future expansion); includes itself
2412 // (1b) entry header len
2413 // (1b) stack frame len
2414 const int kMessageHeaderLen = 15;
2415 const int kEntryHeaderLen = 9;
2416 const int kStackFrameLen = 8;
2417 JDWP::Append1BE(bytes, kMessageHeaderLen);
2418 JDWP::Append1BE(bytes, kEntryHeaderLen);
2419 JDWP::Append1BE(bytes, kStackFrameLen);
2420
2421 // (2b) number of entries
2422 // (4b) offset to string table from start of message
2423 // (2b) number of class name strings
2424 // (2b) number of method name strings
2425 // (2b) number of source file name strings
2426 JDWP::Append2BE(bytes, gAllocRecordCount);
2427 size_t string_table_offset = bytes.size();
2428 JDWP::Append4BE(bytes, 0); // We'll patch this later...
2429 JDWP::Append2BE(bytes, class_names.Size());
2430 JDWP::Append2BE(bytes, method_names.Size());
2431 JDWP::Append2BE(bytes, filenames.Size());
2432
2433 count = gAllocRecordCount;
2434 idx = headIndex();
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002435 ClassHelper kh;
Elliott Hughes545a0642011-11-08 19:10:03 -08002436 while (count--) {
2437 // For each entry:
2438 // (4b) total allocation size
2439 // (2b) thread id
2440 // (2b) allocated object's class name index
2441 // (1b) stack depth
2442 AllocRecord* record = &recent_allocation_records_[idx];
2443 size_t stack_depth = record->GetDepth();
2444 JDWP::Append4BE(bytes, record->byte_count);
2445 JDWP::Append2BE(bytes, record->thin_lock_id);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002446 kh.ChangeClass(record->type);
2447 JDWP::Append2BE(bytes, class_names.IndexOf(kh.GetDescriptor().c_str()));
Elliott Hughes545a0642011-11-08 19:10:03 -08002448 JDWP::Append1BE(bytes, stack_depth);
2449
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002450 MethodHelper mh;
Elliott Hughes545a0642011-11-08 19:10:03 -08002451 for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
2452 // For each stack frame:
2453 // (2b) method's class name
2454 // (2b) method name
2455 // (2b) method source file
2456 // (2b) line number, clipped to 32767; -2 if native; -1 if no source
Ian Rogers6d4d9fc2011-11-30 16:24:48 -08002457 mh.ChangeMethod(record->stack[stack_frame].method);
2458 JDWP::Append2BE(bytes, class_names.IndexOf(mh.GetDeclaringClassDescriptor()));
2459 JDWP::Append2BE(bytes, method_names.IndexOf(mh.GetName()));
2460 JDWP::Append2BE(bytes, filenames.IndexOf(mh.GetDeclaringClassSourceFile()));
Elliott Hughes545a0642011-11-08 19:10:03 -08002461 JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
2462 }
2463
2464 idx = (idx + 1) & (kNumAllocRecords-1);
2465 }
2466
2467 // (xb) class name strings
2468 // (xb) method name strings
2469 // (xb) source file strings
2470 JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
2471 class_names.WriteTo(bytes);
2472 method_names.WriteTo(bytes);
2473 filenames.WriteTo(bytes);
2474
2475 JNIEnv* env = Thread::Current()->GetJniEnv();
2476 jbyteArray result = env->NewByteArray(bytes.size());
2477 if (result != NULL) {
2478 env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
2479 }
2480 return result;
2481}
2482
Elliott Hughes872d4ec2011-10-21 17:07:15 -07002483} // namespace art