blob: 1ad8cc262c6b1445ca553aa12c5272c6d9dd6d3a [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 Hughes6a5bd492011-10-28 14:33:57 -070024#include "ScopedLocalRef.h"
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -070025#include "ScopedPrimitiveArray.h"
Elliott Hughes47fce012011-10-25 18:37:19 -070026#include "stack_indirect_reference_table.h"
Elliott Hughes475fc232011-10-25 15:00:35 -070027#include "thread_list.h"
28
Elliott Hughes6a5bd492011-10-28 14:33:57 -070029extern "C" void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*);
30#ifndef HAVE_ANDROID_OS
31void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*) {
32 // No-op for glibc.
33}
34#endif
35
Elliott Hughes872d4ec2011-10-21 17:07:15 -070036namespace art {
37
Elliott Hughes545a0642011-11-08 19:10:03 -080038static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
39static const size_t kNumAllocRecords = 512; // Must be power of 2.
40
Elliott Hughes475fc232011-10-25 15:00:35 -070041class ObjectRegistry {
42 public:
43 ObjectRegistry() : lock_("ObjectRegistry lock") {
44 }
45
46 JDWP::ObjectId Add(Object* o) {
47 if (o == NULL) {
48 return 0;
49 }
50 JDWP::ObjectId id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(o));
51 MutexLock mu(lock_);
52 map_[id] = o;
53 return id;
54 }
55
Elliott Hughes234ab152011-10-26 14:02:26 -070056 void Clear() {
57 MutexLock mu(lock_);
58 LOG(DEBUG) << "Debugger has detached; object registry had " << map_.size() << " entries";
59 map_.clear();
60 }
61
Elliott Hughes475fc232011-10-25 15:00:35 -070062 bool Contains(JDWP::ObjectId id) {
63 MutexLock mu(lock_);
64 return map_.find(id) != map_.end();
65 }
66
Elliott Hughesa2155262011-11-16 16:26:58 -080067 template<typename T> T Get(JDWP::ObjectId id) {
68 MutexLock mu(lock_);
69 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
70 It it = map_.find(id);
71 return (it != map_.end()) ? reinterpret_cast<T>(it->second) : NULL;
72 }
73
Elliott Hughesbfe487b2011-10-26 15:48:55 -070074 void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
75 MutexLock mu(lock_);
76 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
77 for (It it = map_.begin(); it != map_.end(); ++it) {
78 visitor(it->second, arg);
79 }
80 }
81
Elliott Hughes475fc232011-10-25 15:00:35 -070082 private:
83 Mutex lock_;
84 std::map<JDWP::ObjectId, Object*> map_;
85};
86
Elliott Hughes545a0642011-11-08 19:10:03 -080087struct AllocRecordStackTraceElement {
88 const Method* method;
89 uintptr_t raw_pc;
90
91 int32_t LineNumber() const {
92 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
93 Class* c = method->GetDeclaringClass();
94 DexCache* dex_cache = c->GetDexCache();
95 const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
96 return dex_file.GetLineNumFromPC(method, method->ToDexPC(raw_pc));
97 }
98};
99
100struct AllocRecord {
101 Class* type;
102 size_t byte_count;
103 uint16_t thin_lock_id;
104 AllocRecordStackTraceElement stack[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
105
106 size_t GetDepth() {
107 size_t depth = 0;
108 while (depth < kMaxAllocRecordStackDepth && stack[depth].method != NULL) {
109 ++depth;
110 }
111 return depth;
112 }
113};
114
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700115// JDWP is allowed unless the Zygote forbids it.
116static bool gJdwpAllowed = true;
117
Elliott Hughes3bb81562011-10-21 18:52:59 -0700118// Was there a -Xrunjdwp or -agent argument on the command-line?
119static bool gJdwpConfigured = false;
120
121// Broken-down JDWP options. (Only valid if gJdwpConfigured is true.)
Elliott Hughes376a7a02011-10-24 18:35:55 -0700122static JDWP::JdwpOptions gJdwpOptions;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700123
124// Runtime JDWP state.
125static JDWP::JdwpState* gJdwpState = NULL;
126static bool gDebuggerConnected; // debugger or DDMS is connected.
127static bool gDebuggerActive; // debugger is making requests.
128
Elliott Hughes47fce012011-10-25 18:37:19 -0700129static bool gDdmThreadNotification = false;
130
Elliott Hughes767a1472011-10-26 18:49:02 -0700131// DDMS GC-related settings.
132static Dbg::HpifWhen gDdmHpifWhen = Dbg::HPIF_WHEN_NEVER;
133static Dbg::HpsgWhen gDdmHpsgWhen = Dbg::HPSG_WHEN_NEVER;
134static Dbg::HpsgWhat gDdmHpsgWhat;
135static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
136static Dbg::HpsgWhat gDdmNhsgWhat;
137
Elliott Hughes475fc232011-10-25 15:00:35 -0700138static ObjectRegistry* gRegistry = NULL;
139
Elliott Hughes545a0642011-11-08 19:10:03 -0800140// Recent allocation tracking.
141static Mutex gAllocTrackerLock("AllocTracker lock");
142AllocRecord* Dbg::recent_allocation_records_ = NULL; // TODO: CircularBuffer<AllocRecord>
143static size_t gAllocRecordHead = 0;
144static size_t gAllocRecordCount = 0;
145
Elliott Hughes3bb81562011-10-21 18:52:59 -0700146/*
147 * Handle one of the JDWP name/value pairs.
148 *
149 * JDWP options are:
150 * help: if specified, show help message and bail
151 * transport: may be dt_socket or dt_shmem
152 * address: for dt_socket, "host:port", or just "port" when listening
153 * server: if "y", wait for debugger to attach; if "n", attach to debugger
154 * timeout: how long to wait for debugger to connect / listen
155 *
156 * Useful with server=n (these aren't supported yet):
157 * onthrow=<exception-name>: connect to debugger when exception thrown
158 * onuncaught=y|n: connect to debugger when uncaught exception thrown
159 * launch=<command-line>: launch the debugger itself
160 *
161 * The "transport" option is required, as is "address" if server=n.
162 */
163static bool ParseJdwpOption(const std::string& name, const std::string& value) {
164 if (name == "transport") {
165 if (value == "dt_socket") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700166 gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700167 } else if (value == "dt_android_adb") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700168 gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700169 } else {
170 LOG(ERROR) << "JDWP transport not supported: " << value;
171 return false;
172 }
173 } else if (name == "server") {
174 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700175 gJdwpOptions.server = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700176 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700177 gJdwpOptions.server = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700178 } else {
179 LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
180 return false;
181 }
182 } else if (name == "suspend") {
183 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700184 gJdwpOptions.suspend = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700185 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700186 gJdwpOptions.suspend = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700187 } else {
188 LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
189 return false;
190 }
191 } else if (name == "address") {
192 /* this is either <port> or <host>:<port> */
193 std::string port_string;
Elliott Hughes376a7a02011-10-24 18:35:55 -0700194 gJdwpOptions.host.clear();
Elliott Hughes3bb81562011-10-21 18:52:59 -0700195 std::string::size_type colon = value.find(':');
196 if (colon != std::string::npos) {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700197 gJdwpOptions.host = value.substr(0, colon);
Elliott Hughes3bb81562011-10-21 18:52:59 -0700198 port_string = value.substr(colon + 1);
199 } else {
200 port_string = value;
201 }
202 if (port_string.empty()) {
203 LOG(ERROR) << "JDWP address missing port: " << value;
204 return false;
205 }
206 char* end;
207 long port = strtol(port_string.c_str(), &end, 10);
208 if (*end != '\0') {
209 LOG(ERROR) << "JDWP address has junk in port field: " << value;
210 return false;
211 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700212 gJdwpOptions.port = port;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700213 } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
214 /* valid but unsupported */
215 LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
216 } else {
217 LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
218 }
219
220 return true;
221}
222
223/*
224 * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
225 * "transport=dt_socket,address=8000,server=y,suspend=n"
226 */
227bool Dbg::ParseJdwpOptions(const std::string& options) {
Elliott Hughes47fce012011-10-25 18:37:19 -0700228 LOG(VERBOSE) << "ParseJdwpOptions: " << options;
229
Elliott Hughes3bb81562011-10-21 18:52:59 -0700230 std::vector<std::string> pairs;
231 Split(options, ',', pairs);
232
233 for (size_t i = 0; i < pairs.size(); ++i) {
234 std::string::size_type equals = pairs[i].find('=');
235 if (equals == std::string::npos) {
236 LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
237 return false;
238 }
239 ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
240 }
241
Elliott Hughes376a7a02011-10-24 18:35:55 -0700242 if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700243 LOG(ERROR) << "Must specify JDWP transport: " << options;
244 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700245 if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700246 LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
247 return false;
248 }
249
250 gJdwpConfigured = true;
251 return true;
252}
253
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700254void Dbg::StartJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700255 if (!gJdwpAllowed || !gJdwpConfigured) {
256 // No JDWP for you!
257 return;
258 }
259
Elliott Hughes475fc232011-10-25 15:00:35 -0700260 CHECK(gRegistry == NULL);
261 gRegistry = new ObjectRegistry;
262
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700263 // Init JDWP if the debugger is enabled. This may connect out to a
264 // debugger, passively listen for a debugger, or block waiting for a
265 // debugger.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700266 gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
267 if (gJdwpState == NULL) {
268 LOG(WARNING) << "debugger thread failed to initialize";
Elliott Hughes475fc232011-10-25 15:00:35 -0700269 return;
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700270 }
271
272 // If a debugger has already attached, send the "welcome" message.
273 // This may cause us to suspend all threads.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700274 if (gJdwpState->IsActive()) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800275 //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Elliott Hughes376a7a02011-10-24 18:35:55 -0700276 if (!gJdwpState->PostVMStart()) {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700277 LOG(WARNING) << "failed to post 'start' message to debugger";
278 }
279 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700280}
281
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700282void Dbg::StopJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700283 delete gJdwpState;
Elliott Hughes475fc232011-10-25 15:00:35 -0700284 delete gRegistry;
285 gRegistry = NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700286}
287
Elliott Hughes767a1472011-10-26 18:49:02 -0700288void Dbg::GcDidFinish() {
289 if (gDdmHpifWhen != HPIF_WHEN_NEVER) {
290 LOG(DEBUG) << "Sending VM heap info to DDM";
Elliott Hughes7162ad92011-10-27 14:08:42 -0700291 DdmSendHeapInfo(gDdmHpifWhen);
Elliott Hughes767a1472011-10-26 18:49:02 -0700292 }
293 if (gDdmHpsgWhen != HPSG_WHEN_NEVER) {
294 LOG(DEBUG) << "Dumping VM heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700295 DdmSendHeapSegments(false);
Elliott Hughes767a1472011-10-26 18:49:02 -0700296 }
297 if (gDdmNhsgWhen != HPSG_WHEN_NEVER) {
298 LOG(DEBUG) << "Dumping native heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700299 DdmSendHeapSegments(true);
Elliott Hughes767a1472011-10-26 18:49:02 -0700300 }
301}
302
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700303void Dbg::SetJdwpAllowed(bool allowed) {
304 gJdwpAllowed = allowed;
305}
306
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700307DebugInvokeReq* Dbg::GetInvokeReq() {
Elliott Hughes475fc232011-10-25 15:00:35 -0700308 return Thread::Current()->GetInvokeReq();
309}
310
311Thread* Dbg::GetDebugThread() {
312 return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
313}
314
315void Dbg::ClearWaitForEventThread() {
316 gJdwpState->ClearWaitForEventThread();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700317}
318
319void Dbg::Connected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700320 CHECK(!gDebuggerConnected);
321 LOG(VERBOSE) << "JDWP has attached";
322 gDebuggerConnected = true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700323}
324
Elliott Hughesa2155262011-11-16 16:26:58 -0800325void Dbg::GoActive() {
326 // Enable all debugging features, including scans for breakpoints.
327 // This is a no-op if we're already active.
328 // Only called from the JDWP handler thread.
329 if (gDebuggerActive) {
330 return;
331 }
332
333 LOG(INFO) << "Debugger is active";
334
335 // TODO: CHECK we don't have any outstanding breakpoints.
336
337 gDebuggerActive = true;
338
339 //dvmEnableAllSubMode(kSubModeDebuggerActive);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700340}
341
342void Dbg::Disconnected() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700343 CHECK(gDebuggerConnected);
344
345 gDebuggerActive = false;
346
347 //dvmDisableAllSubMode(kSubModeDebuggerActive);
348
349 gRegistry->Clear();
350 gDebuggerConnected = false;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700351}
352
353bool Dbg::IsDebuggerConnected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700354 return gDebuggerActive;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700355}
356
357bool Dbg::IsDebuggingEnabled() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700358 return gJdwpConfigured;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700359}
360
361int64_t Dbg::LastDebuggerActivity() {
362 UNIMPLEMENTED(WARNING);
363 return -1;
364}
365
366int Dbg::ThreadRunning() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700367 return static_cast<int>(Thread::Current()->SetState(Thread::kRunnable));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700368}
369
370int Dbg::ThreadWaiting() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700371 return static_cast<int>(Thread::Current()->SetState(Thread::kVmWait));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700372}
373
Elliott Hughes6ba581a2011-10-25 11:45:35 -0700374int Dbg::ThreadContinuing(int new_state) {
375 return static_cast<int>(Thread::Current()->SetState(static_cast<Thread::State>(new_state)));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700376}
377
378void Dbg::UndoDebuggerSuspensions() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700379 Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700380}
381
382void Dbg::Exit(int status) {
383 UNIMPLEMENTED(FATAL);
384}
385
Elliott Hughesbfe487b2011-10-26 15:48:55 -0700386void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
387 if (gRegistry != NULL) {
388 gRegistry->VisitRoots(visitor, arg);
389 }
390}
391
Elliott Hughesa2155262011-11-16 16:26:58 -0800392std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
393 Class* c = gRegistry->Get<Class*>(classId);
394 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700395}
396
397JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
398 UNIMPLEMENTED(FATAL);
399 return 0;
400}
401
402JDWP::RefTypeId Dbg::GetSuperclass(JDWP::RefTypeId id) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800403 Class* c = gRegistry->Get<Class*>(id);
404 return gRegistry->Add(c->GetSuperClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700405}
406
407JDWP::ObjectId Dbg::GetClassLoader(JDWP::RefTypeId id) {
408 UNIMPLEMENTED(FATAL);
409 return 0;
410}
411
412uint32_t Dbg::GetAccessFlags(JDWP::RefTypeId id) {
413 UNIMPLEMENTED(FATAL);
414 return 0;
415}
416
417bool Dbg::IsInterface(JDWP::RefTypeId id) {
418 UNIMPLEMENTED(FATAL);
419 return false;
420}
421
Elliott Hughesa2155262011-11-16 16:26:58 -0800422void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
423 // Get the complete list of reference classes (i.e. all classes except
424 // the primitive types).
425 // Returns a newly-allocated buffer full of RefTypeId values.
426 struct ClassListCreator {
427 static bool Visit(Class* c, void* arg) {
428 return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
429 }
430
431 bool Visit(Class* c) {
432 if (!c->IsPrimitive()) {
433 classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
434 }
435 return true;
436 }
437
438 std::vector<JDWP::RefTypeId> classes;
439 };
440
441 ClassListCreator clc;
442 Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
443 *pClassCount = clc.classes.size();
444 *pClasses = new JDWP::RefTypeId[clc.classes.size()];
445 for (size_t i = 0; i < clc.classes.size(); ++i) {
446 (*pClasses)[i] = clc.classes[i];
447 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700448}
449
450void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
451 UNIMPLEMENTED(FATAL);
452}
453
Elliott Hughesa2155262011-11-16 16:26:58 -0800454void Dbg::GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
455 Class* c = gRegistry->Get<Class*>(classId);
456 if (c->IsArrayClass()) {
457 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
458 *pTypeTag = JDWP::TT_ARRAY;
459 } else {
460 if (c->IsErroneous()) {
461 *pStatus = JDWP::CS_ERROR;
462 } else {
463 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
464 }
465 *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
466 }
467
468 if (pDescriptor != NULL) {
469 *pDescriptor = c->GetDescriptor()->ToModifiedUtf8();
470 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700471}
472
473bool Dbg::FindLoadedClassBySignature(const char* classDescriptor, JDWP::RefTypeId* pRefTypeId) {
474 UNIMPLEMENTED(FATAL);
475 return false;
476}
477
478void Dbg::GetObjectType(JDWP::ObjectId objectId, uint8_t* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800479 Object* o = gRegistry->Get<Object*>(objectId);
480 if (o->GetClass()->IsArrayClass()) {
481 *pRefTypeTag = JDWP::TT_ARRAY;
482 } else if (o->GetClass()->IsInterface()) {
483 *pRefTypeTag = JDWP::TT_INTERFACE;
484 } else {
485 *pRefTypeTag = JDWP::TT_CLASS;
486 }
487 *pRefTypeId = gRegistry->Add(o->GetClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700488}
489
490uint8_t Dbg::GetClassObjectType(JDWP::RefTypeId refTypeId) {
491 UNIMPLEMENTED(FATAL);
492 return 0;
493}
494
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800495std::string Dbg::GetSignature(JDWP::RefTypeId refTypeId) {
496 Class* c = gRegistry->Get<Class*>(refTypeId);
497 CHECK(c != NULL);
498 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700499}
500
Elliott Hughes03181a82011-11-17 17:22:21 -0800501bool Dbg::GetSourceFile(JDWP::RefTypeId refTypeId, std::string& result) {
502 Class* c = gRegistry->Get<Class*>(refTypeId);
503 CHECK(c != NULL);
504
505 String* source_file = c->GetSourceFile();
506 if (source_file == NULL) {
507 return false;
508 }
509 result = source_file->ToModifiedUtf8();
510 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700511}
512
513const char* Dbg::GetObjectTypeName(JDWP::ObjectId objectId) {
514 UNIMPLEMENTED(FATAL);
515 return NULL;
516}
517
518uint8_t Dbg::GetObjectTag(JDWP::ObjectId objectId) {
519 UNIMPLEMENTED(FATAL);
520 return 0;
521}
522
Elliott Hughesdbb40792011-11-18 17:05:22 -0800523size_t Dbg::GetTagWidth(int tag) {
524 switch (tag) {
525 case JDWP::JT_VOID:
526 return 0;
527 case JDWP::JT_BYTE:
528 case JDWP::JT_BOOLEAN:
529 return 1;
530 case JDWP::JT_CHAR:
531 case JDWP::JT_SHORT:
532 return 2;
533 case JDWP::JT_FLOAT:
534 case JDWP::JT_INT:
535 return 4;
536 case JDWP::JT_ARRAY:
537 case JDWP::JT_OBJECT:
538 case JDWP::JT_STRING:
539 case JDWP::JT_THREAD:
540 case JDWP::JT_THREAD_GROUP:
541 case JDWP::JT_CLASS_LOADER:
542 case JDWP::JT_CLASS_OBJECT:
543 return sizeof(JDWP::ObjectId);
544 case JDWP::JT_DOUBLE:
545 case JDWP::JT_LONG:
546 return 8;
547 default:
548 LOG(FATAL) << "unknown tag " << tag;
549 return -1;
550 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700551}
552
553int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
554 UNIMPLEMENTED(FATAL);
555 return 0;
556}
557
558uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
559 UNIMPLEMENTED(FATAL);
560 return 0;
561}
562
563bool Dbg::OutputArray(JDWP::ObjectId arrayId, int firstIndex, int count, JDWP::ExpandBuf* pReply) {
564 UNIMPLEMENTED(FATAL);
565 return false;
566}
567
568bool Dbg::SetArrayElements(JDWP::ObjectId arrayId, int firstIndex, int count, const uint8_t* buf) {
569 UNIMPLEMENTED(FATAL);
570 return false;
571}
572
573JDWP::ObjectId Dbg::CreateString(const char* str) {
574 UNIMPLEMENTED(FATAL);
575 return 0;
576}
577
578JDWP::ObjectId Dbg::CreateObject(JDWP::RefTypeId classId) {
579 UNIMPLEMENTED(FATAL);
580 return 0;
581}
582
583JDWP::ObjectId Dbg::CreateArrayObject(JDWP::RefTypeId arrayTypeId, uint32_t length) {
584 UNIMPLEMENTED(FATAL);
585 return 0;
586}
587
588bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
589 UNIMPLEMENTED(FATAL);
590 return false;
591}
592
Elliott Hughes03181a82011-11-17 17:22:21 -0800593JDWP::FieldId ToFieldId(Field* f) {
594#ifdef MOVING_GARBAGE_COLLECTOR
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700595 UNIMPLEMENTED(FATAL);
Elliott Hughes03181a82011-11-17 17:22:21 -0800596#else
597 return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
598#endif
599}
600
601JDWP::MethodId ToMethodId(Method* m) {
602#ifdef MOVING_GARBAGE_COLLECTOR
603 UNIMPLEMENTED(FATAL);
604#else
605 return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
606#endif
607}
608
609Method* FromMethodId(JDWP::MethodId mid) {
610#ifdef MOVING_GARBAGE_COLLECTOR
611 UNIMPLEMENTED(FATAL);
612#else
613 return reinterpret_cast<Method*>(static_cast<uintptr_t>(mid));
614#endif
615}
616
617std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
618 return FromMethodId(methodId)->GetName()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700619}
620
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800621/*
622 * Augment the access flags for synthetic methods and fields by setting
623 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
624 * flags not specified by the Java programming language.
625 */
626static uint32_t MangleAccessFlags(uint32_t accessFlags) {
627 accessFlags &= kAccJavaFlagsMask;
628 if ((accessFlags & kAccSynthetic) != 0) {
629 accessFlags |= 0xf0000000;
630 }
631 return accessFlags;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700632}
633
Elliott Hughesdbb40792011-11-18 17:05:22 -0800634static JDWP::JdwpTag TagFromClass(Class* c) {
635 if (c->IsArrayClass()) {
636 return JDWP::JT_ARRAY;
637 }
638
639 if (c->IsStringClass()) {
640 return JDWP::JT_STRING;
641 } else if (c->IsClassClass()) {
642 return JDWP::JT_CLASS_OBJECT;
643#if 0 // TODO
644 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
645 return JDWP::JT_THREAD;
646 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
647 return JDWP::JT_THREAD_GROUP;
648 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
649 return JDWP::JT_CLASS_LOADER;
650#endif
651 } else {
652 return JDWP::JT_OBJECT;
653 }
654}
655
656/*
657 * Objects declared to hold Object might actually hold a more specific
658 * type. The debugger may take a special interest in these (e.g. it
659 * wants to display the contents of Strings), so we want to return an
660 * appropriate tag.
661 *
662 * Null objects are tagged JT_OBJECT.
663 */
664static JDWP::JdwpTag TagFromObject(const Object* o) {
665 return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
666}
667
668static const uint16_t kEclipseWorkaroundSlot = 1000;
669
670/*
671 * Eclipse appears to expect that the "this" reference is in slot zero.
672 * If it's not, the "variables" display will show two copies of "this",
673 * possibly because it gets "this" from SF.ThisObject and then displays
674 * all locals with nonzero slot numbers.
675 *
676 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
677 * SF.GetValues / SF.SetValues we map them back.
678 */
679static uint16_t MangleSlot(uint16_t slot, const char* name) {
680 uint16_t newSlot = slot;
681 if (strcmp(name, "this") == 0) {
682 newSlot = 0;
683 } else if (slot == 0) {
684 newSlot = kEclipseWorkaroundSlot;
685 }
686 return newSlot;
687}
688
689/*
690 * Reverse Eclipse hack.
691 */
692static uint16_t DemangleSlot(uint16_t slot, Method** sp) {
693 int newSlot = slot;
694 if (slot == kEclipseWorkaroundSlot) {
695 newSlot = 0;
696 } else if (slot == 0) {
697 Frame f;
698 f.SetSP(sp);
699 Method* m = f.GetMethod();
700 newSlot = m->NumRegisters() - m->NumIns();
701 }
702 return newSlot;
703}
704
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800705void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
706 Class* c = gRegistry->Get<Class*>(refTypeId);
707 CHECK(c != NULL);
708
709 size_t instance_field_count = c->NumInstanceFields();
710 size_t static_field_count = c->NumStaticFields();
711
712 expandBufAdd4BE(pReply, instance_field_count + static_field_count);
713
714 for (size_t i = 0; i < instance_field_count + static_field_count; ++i) {
715 Field* f = (i < instance_field_count) ? c->GetInstanceField(i) : c->GetStaticField(i - instance_field_count);
716
717 expandBufAddFieldId(pReply, ToFieldId(f));
718 expandBufAddUtf8String(pReply, f->GetName()->ToModifiedUtf8().c_str());
719 expandBufAddUtf8String(pReply, f->GetTypeDescriptor());
720 if (withGeneric) {
721 static const char genericSignature[1] = "";
722 expandBufAddUtf8String(pReply, genericSignature);
723 }
724 expandBufAdd4BE(pReply, MangleAccessFlags(f->GetAccessFlags()));
725 }
726}
727
728void Dbg::OutputDeclaredMethods(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
729 Class* c = gRegistry->Get<Class*>(refTypeId);
730 CHECK(c != NULL);
731
732 size_t direct_method_count = c->NumDirectMethods();
733 size_t virtual_method_count = c->NumVirtualMethods();
734
735 expandBufAdd4BE(pReply, direct_method_count + virtual_method_count);
736
737 for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) {
738 Method* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count);
739
740 expandBufAddMethodId(pReply, ToMethodId(m));
741 expandBufAddUtf8String(pReply, m->GetName()->ToModifiedUtf8().c_str());
742 expandBufAddUtf8String(pReply, m->GetSignature()->ToModifiedUtf8().c_str());
743 if (withGeneric) {
744 static const char genericSignature[1] = "";
745 expandBufAddUtf8String(pReply, genericSignature);
746 }
747 expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags()));
748 }
749}
750
751void Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId refTypeId, JDWP::ExpandBuf* pReply) {
752 Class* c = gRegistry->Get<Class*>(refTypeId);
753 CHECK(c != NULL);
754 size_t interface_count = c->NumInterfaces();
755 expandBufAdd4BE(pReply, interface_count);
756 for (size_t i = 0; i < interface_count; ++i) {
757 expandBufAddRefTypeId(pReply, gRegistry->Add(c->GetInterface(i)));
758 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700759}
760
761void Dbg::OutputLineTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, JDWP::ExpandBuf* pReply) {
Elliott Hughes03181a82011-11-17 17:22:21 -0800762 struct DebugCallbackContext {
763 int numItems;
764 JDWP::ExpandBuf* pReply;
765
766 static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
767 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
768 expandBufAdd8BE(pContext->pReply, address);
769 expandBufAdd4BE(pContext->pReply, lineNum);
770 pContext->numItems++;
771 return true;
772 }
773 };
774
775 Method* m = FromMethodId(methodId);
776 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
777 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
778 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
779
780 uint64_t start, end;
781 if (m->IsNative()) {
782 start = -1;
783 end = -1;
784 } else {
785 start = 0;
786 end = code_item->insns_size_in_code_units_; // TODO: what are the units supposed to be? *2?
787 }
788
789 expandBufAdd8BE(pReply, start);
790 expandBufAdd8BE(pReply, end);
791
792 // Add numLines later
793 size_t numLinesOffset = expandBufGetLength(pReply);
794 expandBufAdd4BE(pReply, 0);
795
796 DebugCallbackContext context;
797 context.numItems = 0;
798 context.pReply = pReply;
799
800 dex_file.DecodeDebugInfo(code_item, m, DebugCallbackContext::Callback, NULL, &context);
801
802 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700803}
804
Elliott Hughesdbb40792011-11-18 17:05:22 -0800805void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, bool withGeneric, JDWP::ExpandBuf* pReply) {
806 struct DebugCallbackContext {
807 int numItems;
808 JDWP::ExpandBuf* pReply;
809 bool withGeneric;
810
811 static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char *name, const char *descriptor, const char *signature) {
812 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
813
814 slot = MangleSlot(slot, name);
815
816 LOG(VERBOSE) << StringPrintf(" %2d: %d(%d) '%s' '%s' '%s' slot=%d", pContext->numItems, startAddress, endAddress - startAddress, name, descriptor, signature, slot);
817
818 expandBufAdd8BE(pContext->pReply, startAddress);
819 expandBufAddUtf8String(pContext->pReply, name);
820 expandBufAddUtf8String(pContext->pReply, descriptor);
821 if (pContext->withGeneric) {
822 expandBufAddUtf8String(pContext->pReply, signature);
823 }
824 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
825 expandBufAdd4BE(pContext->pReply, slot);
826
827 pContext->numItems++;
828 }
829 };
830
831 Method* m = FromMethodId(methodId);
832 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
833 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
834 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
835
836 expandBufAdd4BE(pReply, m->NumIns());
837
838 // Add numLocals later
839 size_t numLocalsOffset = expandBufGetLength(pReply);
840 expandBufAdd4BE(pReply, 0);
841
842 DebugCallbackContext context;
843 context.numItems = 0;
844 context.pReply = pReply;
845 context.withGeneric = withGeneric;
846
847 dex_file.DecodeDebugInfo(code_item, m, NULL, DebugCallbackContext::Callback, &context);
848
849 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700850}
851
852uint8_t Dbg::GetFieldBasicTag(JDWP::ObjectId objId, JDWP::FieldId fieldId) {
853 UNIMPLEMENTED(FATAL);
854 return 0;
855}
856
857uint8_t Dbg::GetStaticFieldBasicTag(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId) {
858 UNIMPLEMENTED(FATAL);
859 return 0;
860}
861
862void Dbg::GetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
863 UNIMPLEMENTED(FATAL);
864}
865
866void Dbg::SetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, uint64_t value, int width) {
867 UNIMPLEMENTED(FATAL);
868}
869
870void Dbg::GetStaticFieldValue(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
871 UNIMPLEMENTED(FATAL);
872}
873
874void Dbg::SetStaticFieldValue(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId, uint64_t rawValue, int width) {
875 UNIMPLEMENTED(FATAL);
876}
877
878char* Dbg::StringToUtf8(JDWP::ObjectId strId) {
879 UNIMPLEMENTED(FATAL);
880 return NULL;
881}
882
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800883Thread* DecodeThread(JDWP::ObjectId threadId) {
884 Object* thread_peer = gRegistry->Get<Object*>(threadId);
885 CHECK(thread_peer != NULL);
886 return Thread::FromManagedThread(thread_peer);
887}
888
889bool Dbg::GetThreadName(JDWP::ObjectId threadId, std::string& name) {
890 ScopedThreadListLock thread_list_lock;
891 Thread* thread = DecodeThread(threadId);
892 if (thread == NULL) {
893 return false;
894 }
895 StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetName()->ToModifiedUtf8().c_str());
896 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700897}
898
899JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800900 Object* thread = gRegistry->Get<Object*>(threadId);
901 CHECK(thread != NULL);
902
903 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
904 CHECK(c != NULL);
905 Field* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
906 CHECK(f != NULL);
907 Object* group = f->GetObject(thread);
908 CHECK(group != NULL);
909 return gRegistry->Add(group);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700910}
911
Elliott Hughes499c5132011-11-17 14:55:11 -0800912std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
913 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
914 CHECK(thread_group != NULL);
915
916 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
917 CHECK(c != NULL);
918 Field* f = c->FindInstanceField("name", "Ljava/lang/String;");
919 CHECK(f != NULL);
920 String* s = reinterpret_cast<String*>(f->GetObject(thread_group));
921 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700922}
923
924JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
925 UNIMPLEMENTED(FATAL);
926 return 0;
927}
928
Elliott Hughes499c5132011-11-17 14:55:11 -0800929static Object* GetStaticThreadGroup(const char* field_name) {
930 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
931 CHECK(c != NULL);
932 Field* f = c->FindStaticField(field_name, "Ljava/lang/ThreadGroup;");
933 CHECK(f != NULL);
934 Object* group = f->GetObject(NULL);
935 CHECK(group != NULL);
936 return group;
937}
938
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700939JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -0800940 return gRegistry->Add(GetStaticThreadGroup("mSystem"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700941}
942
943JDWP::ObjectId Dbg::GetMainThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -0800944 return gRegistry->Add(GetStaticThreadGroup("mMain"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700945}
946
Elliott Hughes499c5132011-11-17 14:55:11 -0800947bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* pThreadStatus, uint32_t* pSuspendStatus) {
948 ScopedThreadListLock thread_list_lock;
949
950 Thread* thread = DecodeThread(threadId);
951 if (thread == NULL) {
952 return false;
953 }
954
955 switch (thread->GetState()) {
956 case Thread::kTerminated: *pThreadStatus = JDWP::TS_ZOMBIE; break;
957 case Thread::kRunnable: *pThreadStatus = JDWP::TS_RUNNING; break;
958 case Thread::kTimedWaiting: *pThreadStatus = JDWP::TS_SLEEPING; break;
959 case Thread::kBlocked: *pThreadStatus = JDWP::TS_MONITOR; break;
960 case Thread::kWaiting: *pThreadStatus = JDWP::TS_WAIT; break;
961 case Thread::kInitializing: *pThreadStatus = JDWP::TS_ZOMBIE; break;
962 case Thread::kStarting: *pThreadStatus = JDWP::TS_ZOMBIE; break;
963 case Thread::kNative: *pThreadStatus = JDWP::TS_RUNNING; break;
964 case Thread::kVmWait: *pThreadStatus = JDWP::TS_WAIT; break;
965 case Thread::kSuspended: *pThreadStatus = JDWP::TS_RUNNING; break;
966 default:
967 LOG(FATAL) << "unknown thread state " << thread->GetState();
968 }
969
970 *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : 0);
971
972 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700973}
974
975uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
976 UNIMPLEMENTED(FATAL);
977 return 0;
978}
979
980bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -0800981 return DecodeThread(threadId) != NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700982}
983
984bool Dbg::IsSuspended(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -0800985 return DecodeThread(threadId)->IsSuspended();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700986}
987
988//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
989
Elliott Hughesa2155262011-11-16 16:26:58 -0800990void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
991 struct ThreadListVisitor {
992 static void Visit(Thread* t, void* arg) {
993 reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
994 }
995
996 void Visit(Thread* t) {
997 if (t == Dbg::GetDebugThread()) {
998 // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
999 // query all threads, so it's easier if we just don't tell them about this thread.
1000 return;
1001 }
1002 if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
1003 threads.push_back(gRegistry->Add(t->GetPeer()));
1004 }
1005 }
1006
1007 Object* thread_group;
1008 std::vector<JDWP::ObjectId> threads;
1009 };
1010
1011 ThreadListVisitor tlv;
1012 tlv.thread_group = thread_group;
1013
1014 {
1015 ScopedThreadListLock thread_list_lock;
1016 Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
1017 }
1018
1019 *pThreadCount = tlv.threads.size();
1020 if (*pThreadCount == 0) {
1021 *ppThreadIds = NULL;
1022 } else {
1023 *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
1024 for (size_t i = 0; i < *pThreadCount; ++i) {
1025 (*ppThreadIds)[i] = tlv.threads[i];
1026 }
1027 }
1028}
1029
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001030void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001031 GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001032}
1033
1034void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001035 GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001036}
1037
1038int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
Elliott Hughes03181a82011-11-17 17:22:21 -08001039 ScopedThreadListLock thread_list_lock;
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001040 struct CountStackDepthVisitor : public Thread::StackVisitor {
1041 CountStackDepthVisitor() : depth(0) {}
Elliott Hughes03181a82011-11-17 17:22:21 -08001042 virtual void VisitFrame(const Frame&, uintptr_t) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001043 ++depth;
1044 }
1045 size_t depth;
1046 };
1047 CountStackDepthVisitor visitor;
1048 DecodeThread(threadId)->WalkStack(&visitor);
1049 return visitor.depth;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001050}
1051
Elliott Hughes03181a82011-11-17 17:22:21 -08001052bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
1053 ScopedThreadListLock thread_list_lock;
1054 struct GetFrameVisitor : public Thread::StackVisitor {
1055 GetFrameVisitor(int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc)
1056 : found(false) ,depth(0), desired_frame_number(desired_frame_number), pFrameId(pFrameId), pLoc(pLoc) {
1057 }
1058 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
1059 if (!f.HasMethod()) {
1060 return; // These don't count?
1061 }
1062
1063 if (depth == desired_frame_number) {
1064 *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
1065
1066 Method* m = f.GetMethod();
1067 Class* c = m->GetDeclaringClass();
1068
1069 pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
1070 pLoc->classId = gRegistry->Add(c);
1071 pLoc->methodId = ToMethodId(m);
1072 pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
1073
1074 found = true;
1075 }
1076 ++depth;
1077 }
1078 bool found;
1079 int depth;
1080 int desired_frame_number;
1081 JDWP::FrameId* pFrameId;
1082 JDWP::JdwpLocation* pLoc;
1083 };
1084 GetFrameVisitor visitor(desired_frame_number, pFrameId, pLoc);
1085 visitor.desired_frame_number = desired_frame_number;
1086 DecodeThread(threadId)->WalkStack(&visitor);
1087 return visitor.found;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001088}
1089
1090JDWP::ObjectId Dbg::GetThreadSelfId() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001091 return gRegistry->Add(Thread::Current()->GetPeer());
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001092}
1093
Elliott Hughes475fc232011-10-25 15:00:35 -07001094void Dbg::SuspendVM() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001095 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 -07001096 Runtime::Current()->GetThreadList()->SuspendAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001097}
1098
1099void Dbg::ResumeVM() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001100 Runtime::Current()->GetThreadList()->ResumeAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001101}
1102
1103void Dbg::SuspendThread(JDWP::ObjectId threadId) {
1104 UNIMPLEMENTED(FATAL);
1105}
1106
1107void Dbg::ResumeThread(JDWP::ObjectId threadId) {
1108 UNIMPLEMENTED(FATAL);
1109}
1110
1111void Dbg::SuspendSelf() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001112 Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001113}
1114
1115bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
1116 UNIMPLEMENTED(FATAL);
1117 return false;
1118}
1119
Elliott Hughesdbb40792011-11-18 17:05:22 -08001120void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen) {
1121 Method** sp = reinterpret_cast<Method**>(frameId);
1122 slot = DemangleSlot(slot, sp);
1123
1124 switch (tag) {
1125 case JDWP::JT_BOOLEAN:
1126 {
1127 UNIMPLEMENTED(WARNING) << "get boolean local " << slot;
1128 CHECK_EQ(expectedLen, 1U);
1129 uint32_t intVal = 0; // framePtr[slot];
1130 JDWP::Set1(buf+1, intVal != 0);
1131 }
1132 break;
1133 case JDWP::JT_BYTE:
1134 {
1135 UNIMPLEMENTED(WARNING) << "get byte local " << slot;
1136 CHECK_EQ(expectedLen, 1U);
1137 uint32_t intVal = 0; // framePtr[slot];
1138 JDWP::Set1(buf+1, intVal);
1139 }
1140 break;
1141 case JDWP::JT_SHORT:
1142 case JDWP::JT_CHAR:
1143 {
1144 UNIMPLEMENTED(WARNING) << "get 16-bit local " << slot;
1145 CHECK_EQ(expectedLen, 2U);
1146 uint32_t intVal = 0; // framePtr[slot];
1147 JDWP::Set2BE(buf+1, intVal);
1148 }
1149 break;
1150 case JDWP::JT_INT:
1151 case JDWP::JT_FLOAT:
1152 {
1153 UNIMPLEMENTED(WARNING) << "get 32-bit local " << slot;
1154 CHECK_EQ(expectedLen, 4U);
1155 uint32_t intVal = 0; // framePtr[slot];
1156 JDWP::Set4BE(buf+1, intVal);
1157 }
1158 break;
1159 case JDWP::JT_ARRAY:
1160 {
1161 UNIMPLEMENTED(WARNING) << "get array local " << slot;
1162 CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
1163 Object* o = NULL; // (Object*)framePtr[slot];
1164 if (o != NULL && !Heap::IsHeapAddress(o)) {
1165 LOG(FATAL) << "slot " << slot << " expected to hold array: " << o;
1166 }
1167 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1168 }
1169 break;
1170 case JDWP::JT_OBJECT:
1171 {
1172 UNIMPLEMENTED(WARNING) << "get object local " << slot;
1173 CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
1174 Object* o = NULL; // (Object*)framePtr[slot];
1175 if (o != NULL && !Heap::IsHeapAddress(o)) {
1176 LOG(FATAL) << "slot " << slot << " expected to hold object: " << o;
1177 }
1178 tag = TagFromObject(o);
1179 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1180 }
1181 break;
1182 case JDWP::JT_DOUBLE:
1183 case JDWP::JT_LONG:
1184 {
1185 UNIMPLEMENTED(WARNING) << "get 64-bit local " << slot;
1186 CHECK_EQ(expectedLen, 8U);
1187 uint64_t longVal = 0; // memcpy(&longVal, &framePtr[slot], 8);
1188 JDWP::Set8BE(buf+1, longVal);
1189 }
1190 break;
1191 default:
1192 LOG(FATAL) << "unknown tag " << tag;
1193 break;
1194 }
1195
1196 // Prepend tag, which may have been updated.
1197 JDWP::Set1(buf, tag);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001198}
1199
Elliott Hughesdbb40792011-11-18 17:05:22 -08001200void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width) {
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001201 UNIMPLEMENTED(FATAL);
1202}
1203
1204void Dbg::PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags) {
1205 UNIMPLEMENTED(FATAL);
1206}
1207
1208void Dbg::PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception) {
1209 UNIMPLEMENTED(FATAL);
1210}
1211
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001212void Dbg::PostClassPrepare(Class* c) {
1213 UNIMPLEMENTED(FATAL);
1214}
1215
1216bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
1217 UNIMPLEMENTED(FATAL);
1218 return false;
1219}
1220
1221void Dbg::UnwatchLocation(const JDWP::JdwpLocation* pLoc) {
1222 UNIMPLEMENTED(FATAL);
1223}
1224
1225bool Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) {
1226 UNIMPLEMENTED(FATAL);
1227 return false;
1228}
1229
1230void Dbg::UnconfigureStep(JDWP::ObjectId threadId) {
1231 UNIMPLEMENTED(FATAL);
1232}
1233
1234JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, uint8_t* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptObj) {
1235 UNIMPLEMENTED(FATAL);
1236 return JDWP::ERR_NONE;
1237}
1238
1239void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
1240 UNIMPLEMENTED(FATAL);
1241}
1242
1243void Dbg::RegisterObjectId(JDWP::ObjectId id) {
1244 UNIMPLEMENTED(FATAL);
1245}
1246
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001247/*
1248 * "buf" contains a full JDWP packet, possibly with multiple chunks. We
1249 * need to process each, accumulate the replies, and ship the whole thing
1250 * back.
1251 *
1252 * Returns "true" if we have a reply. The reply buffer is newly allocated,
1253 * and includes the chunk type/length, followed by the data.
1254 *
1255 * TODO: we currently assume that the request and reply include a single
1256 * chunk. If this becomes inconvenient we will need to adapt.
1257 */
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001258bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001259 CHECK_GE(dataLen, 0);
1260
1261 Thread* self = Thread::Current();
1262 JNIEnv* env = self->GetJniEnv();
1263
1264 static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
1265 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1266 static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
1267 "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
1268 static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
1269 static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
1270 static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
1271 static jfieldID type_fid = env->GetFieldID(Chunk_class, "type", "I");
1272
1273 // Create a byte[] corresponding to 'buf'.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001274 ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(dataLen));
1275 if (dataArray.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001276 LOG(WARNING) << "byte[] allocation failed: " << dataLen;
1277 env->ExceptionClear();
1278 return false;
1279 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001280 env->SetByteArrayRegion(dataArray.get(), 0, dataLen, reinterpret_cast<const jbyte*>(buf));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001281
1282 const int kChunkHdrLen = 8;
1283
1284 // Run through and find all chunks. [Currently just find the first.]
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001285 ScopedByteArrayRO contents(env, dataArray.get());
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001286 jint type = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[0]));
1287 jint length = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[4]));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001288 jint offset = kChunkHdrLen;
1289 if (offset + length > dataLen) {
1290 LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%d)", length, dataLen);
1291 return false;
1292 }
1293
1294 // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001295 ScopedLocalRef<jobject> chunk(env, env->CallStaticObjectMethod(DdmServer_class, dispatch_mid, type, dataArray.get(), offset, length));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001296 if (env->ExceptionCheck()) {
1297 LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
1298 env->ExceptionDescribe();
1299 env->ExceptionClear();
1300 return false;
1301 }
1302
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001303 if (chunk.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001304 return false;
1305 }
1306
1307 /*
1308 * Pull the pieces out of the chunk. We copy the results into a
1309 * newly-allocated buffer that the caller can free. We don't want to
1310 * continue using the Chunk object because nothing has a reference to it.
1311 *
1312 * We could avoid this by returning type/data/offset/length and having
1313 * the caller be aware of the object lifetime issues, but that
1314 * integrates the JDWP code more tightly into the VM, and doesn't work
1315 * if we have responses for multiple chunks.
1316 *
1317 * So we're pretty much stuck with copying data around multiple times.
1318 */
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001319 ScopedLocalRef<jbyteArray> replyData(env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk.get(), data_fid)));
1320 length = env->GetIntField(chunk.get(), length_fid);
1321 offset = env->GetIntField(chunk.get(), offset_fid);
1322 type = env->GetIntField(chunk.get(), type_fid);
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001323
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001324 LOG(VERBOSE) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
1325 if (length == 0 || replyData.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001326 return false;
1327 }
1328
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001329 jsize replyLength = env->GetArrayLength(replyData.get());
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001330 if (offset + length > replyLength) {
1331 LOG(WARNING) << StringPrintf("chunk off=%d len=%d exceeds reply array len %d", offset, length, replyLength);
1332 return false;
1333 }
1334
1335 uint8_t* reply = new uint8_t[length + kChunkHdrLen];
1336 if (reply == NULL) {
1337 LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
1338 return false;
1339 }
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001340 JDWP::Set4BE(reply + 0, type);
1341 JDWP::Set4BE(reply + 4, length);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001342 env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001343
1344 *pReplyBuf = reply;
1345 *pReplyLen = length + kChunkHdrLen;
1346
1347 LOG(VERBOSE) << StringPrintf("dvmHandleDdm returning type=%.4s buf=%p len=%d", (char*) reply, reply, length);
1348 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001349}
1350
Elliott Hughesa2155262011-11-16 16:26:58 -08001351void Dbg::DdmBroadcast(bool connect) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001352 LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
1353
1354 Thread* self = Thread::Current();
1355 if (self->GetState() != Thread::kRunnable) {
1356 LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
1357 /* try anyway? */
1358 }
1359
1360 JNIEnv* env = self->GetJniEnv();
1361 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1362 static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
1363 jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
1364 env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
1365 if (env->ExceptionCheck()) {
1366 LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
1367 env->ExceptionDescribe();
1368 env->ExceptionClear();
1369 }
1370}
1371
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001372void Dbg::DdmConnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001373 Dbg::DdmBroadcast(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001374}
1375
1376void Dbg::DdmDisconnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001377 Dbg::DdmBroadcast(false);
Elliott Hughes47fce012011-10-25 18:37:19 -07001378 gDdmThreadNotification = false;
1379}
1380
1381/*
Elliott Hughes82188472011-11-07 18:11:48 -08001382 * Send a notification when a thread starts, stops, or changes its name.
Elliott Hughes47fce012011-10-25 18:37:19 -07001383 *
1384 * Because we broadcast the full set of threads when the notifications are
1385 * first enabled, it's possible for "thread" to be actively executing.
1386 */
Elliott Hughes82188472011-11-07 18:11:48 -08001387void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001388 if (!gDdmThreadNotification) {
1389 return;
1390 }
1391
Elliott Hughes82188472011-11-07 18:11:48 -08001392 if (type == CHUNK_TYPE("THDE")) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001393 uint8_t buf[4];
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001394 JDWP::Set4BE(&buf[0], t->GetThinLockId());
Elliott Hughes47fce012011-10-25 18:37:19 -07001395 Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
Elliott Hughes82188472011-11-07 18:11:48 -08001396 } else {
1397 CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
1398 SirtRef<String> name(t->GetName());
1399 size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
1400 const jchar* chars = name->GetCharArray()->GetData();
1401
Elliott Hughes21f32d72011-11-09 17:44:13 -08001402 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001403 JDWP::Append4BE(bytes, t->GetThinLockId());
1404 JDWP::AppendUtf16BE(bytes, chars, char_count);
Elliott Hughes21f32d72011-11-09 17:44:13 -08001405 CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
1406 Dbg::DdmSendChunk(type, bytes);
Elliott Hughes47fce012011-10-25 18:37:19 -07001407 }
1408}
1409
Elliott Hughesa2155262011-11-16 16:26:58 -08001410static void DdmSendThreadStartCallback(Thread* t, void*) {
Elliott Hughes82188472011-11-07 18:11:48 -08001411 Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001412}
1413
1414void Dbg::DdmSetThreadNotification(bool enable) {
1415 // We lock the thread list to avoid sending duplicate events or missing
1416 // a thread change. We should be okay holding this lock while sending
1417 // the messages out. (We have to hold it while accessing a live thread.)
Elliott Hughesbbd9d832011-11-07 14:40:00 -08001418 ScopedThreadListLock thread_list_lock;
Elliott Hughes47fce012011-10-25 18:37:19 -07001419
1420 gDdmThreadNotification = enable;
1421 if (enable) {
Elliott Hughesbfe487b2011-10-26 15:48:55 -07001422 Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback, NULL);
Elliott Hughes47fce012011-10-25 18:37:19 -07001423 }
1424}
1425
Elliott Hughesa2155262011-11-16 16:26:58 -08001426void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001427 if (gDebuggerActive) {
1428 JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
Elliott Hughes82188472011-11-07 18:11:48 -08001429 gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001430 }
Elliott Hughes82188472011-11-07 18:11:48 -08001431 Dbg::DdmSendThreadNotification(t, type);
Elliott Hughes47fce012011-10-25 18:37:19 -07001432}
1433
1434void Dbg::PostThreadStart(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001435 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001436}
1437
1438void Dbg::PostThreadDeath(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001439 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001440}
1441
Elliott Hughes82188472011-11-07 18:11:48 -08001442void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001443 CHECK(buf != NULL);
1444 iovec vec[1];
1445 vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
1446 vec[0].iov_len = byte_count;
1447 Dbg::DdmSendChunkV(type, vec, 1);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001448}
1449
Elliott Hughes21f32d72011-11-09 17:44:13 -08001450void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
1451 DdmSendChunk(type, bytes.size(), &bytes[0]);
1452}
1453
Elliott Hughes82188472011-11-07 18:11:48 -08001454void Dbg::DdmSendChunkV(uint32_t type, const struct iovec* iov, int iovcnt) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001455 if (gJdwpState == NULL) {
1456 LOG(VERBOSE) << "Debugger thread not active, ignoring DDM send: " << type;
1457 } else {
Elliott Hughes376a7a02011-10-24 18:35:55 -07001458 gJdwpState->DdmSendChunkV(type, iov, iovcnt);
Elliott Hughes3bb81562011-10-21 18:52:59 -07001459 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001460}
1461
Elliott Hughes767a1472011-10-26 18:49:02 -07001462int Dbg::DdmHandleHpifChunk(HpifWhen when) {
1463 if (when == HPIF_WHEN_NOW) {
Elliott Hughes7162ad92011-10-27 14:08:42 -07001464 DdmSendHeapInfo(when);
Elliott Hughes767a1472011-10-26 18:49:02 -07001465 return true;
1466 }
1467
1468 if (when != HPIF_WHEN_NEVER && when != HPIF_WHEN_NEXT_GC && when != HPIF_WHEN_EVERY_GC) {
1469 LOG(ERROR) << "invalid HpifWhen value: " << static_cast<int>(when);
1470 return false;
1471 }
1472
1473 gDdmHpifWhen = when;
1474 return true;
1475}
1476
1477bool Dbg::DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when, Dbg::HpsgWhat what, bool native) {
1478 if (when != HPSG_WHEN_NEVER && when != HPSG_WHEN_EVERY_GC) {
1479 LOG(ERROR) << "invalid HpsgWhen value: " << static_cast<int>(when);
1480 return false;
1481 }
1482
1483 if (what != HPSG_WHAT_MERGED_OBJECTS && what != HPSG_WHAT_DISTINCT_OBJECTS) {
1484 LOG(ERROR) << "invalid HpsgWhat value: " << static_cast<int>(what);
1485 return false;
1486 }
1487
1488 if (native) {
1489 gDdmNhsgWhen = when;
1490 gDdmNhsgWhat = what;
1491 } else {
1492 gDdmHpsgWhen = when;
1493 gDdmHpsgWhat = what;
1494 }
1495 return true;
1496}
1497
Elliott Hughes7162ad92011-10-27 14:08:42 -07001498void Dbg::DdmSendHeapInfo(HpifWhen reason) {
1499 // If there's a one-shot 'when', reset it.
1500 if (reason == gDdmHpifWhen) {
1501 if (gDdmHpifWhen == HPIF_WHEN_NEXT_GC) {
1502 gDdmHpifWhen = HPIF_WHEN_NEVER;
1503 }
1504 }
1505
1506 /*
1507 * Chunk HPIF (client --> server)
1508 *
1509 * Heap Info. General information about the heap,
1510 * suitable for a summary display.
1511 *
1512 * [u4]: number of heaps
1513 *
1514 * For each heap:
1515 * [u4]: heap ID
1516 * [u8]: timestamp in ms since Unix epoch
1517 * [u1]: capture reason (same as 'when' value from server)
1518 * [u4]: max heap size in bytes (-Xmx)
1519 * [u4]: current heap size in bytes
1520 * [u4]: current number of bytes allocated
1521 * [u4]: current number of objects allocated
1522 */
1523 uint8_t heap_count = 1;
Elliott Hughes21f32d72011-11-09 17:44:13 -08001524 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001525 JDWP::Append4BE(bytes, heap_count);
1526 JDWP::Append4BE(bytes, 1); // Heap id (bogus; we only have one heap).
1527 JDWP::Append8BE(bytes, MilliTime());
1528 JDWP::Append1BE(bytes, reason);
1529 JDWP::Append4BE(bytes, Heap::GetMaxMemory()); // Max allowed heap size in bytes.
1530 JDWP::Append4BE(bytes, Heap::GetTotalMemory()); // Current heap size in bytes.
1531 JDWP::Append4BE(bytes, Heap::GetBytesAllocated());
1532 JDWP::Append4BE(bytes, Heap::GetObjectsAllocated());
Elliott Hughes21f32d72011-11-09 17:44:13 -08001533 CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
1534 Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
Elliott Hughes767a1472011-10-26 18:49:02 -07001535}
1536
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001537enum HpsgSolidity {
1538 SOLIDITY_FREE = 0,
1539 SOLIDITY_HARD = 1,
1540 SOLIDITY_SOFT = 2,
1541 SOLIDITY_WEAK = 3,
1542 SOLIDITY_PHANTOM = 4,
1543 SOLIDITY_FINALIZABLE = 5,
1544 SOLIDITY_SWEEP = 6,
1545};
1546
1547enum HpsgKind {
1548 KIND_OBJECT = 0,
1549 KIND_CLASS_OBJECT = 1,
1550 KIND_ARRAY_1 = 2,
1551 KIND_ARRAY_2 = 3,
1552 KIND_ARRAY_4 = 4,
1553 KIND_ARRAY_8 = 5,
1554 KIND_UNKNOWN = 6,
1555 KIND_NATIVE = 7,
1556};
1557
1558#define HPSG_PARTIAL (1<<7)
1559#define HPSG_STATE(solidity, kind) ((uint8_t)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
1560
1561struct HeapChunkContext {
1562 std::vector<uint8_t> buf;
1563 uint8_t* p;
1564 uint8_t* pieceLenField;
1565 size_t totalAllocationUnits;
Elliott Hughes82188472011-11-07 18:11:48 -08001566 uint32_t type;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001567 bool merge;
1568 bool needHeader;
1569
1570 // Maximum chunk size. Obtain this from the formula:
1571 // (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
1572 HeapChunkContext(bool merge, bool native)
1573 : buf(16384 - 16),
1574 type(0),
1575 merge(merge) {
1576 Reset();
1577 if (native) {
1578 type = CHUNK_TYPE("NHSG");
1579 } else {
1580 type = merge ? CHUNK_TYPE("HPSG") : CHUNK_TYPE("HPSO");
1581 }
1582 }
1583
1584 ~HeapChunkContext() {
1585 if (p > &buf[0]) {
1586 Flush();
1587 }
1588 }
1589
1590 void EnsureHeader(const void* chunk_ptr) {
1591 if (!needHeader) {
1592 return;
1593 }
1594
1595 // Start a new HPSx chunk.
1596 JDWP::Write4BE(&p, 1); // Heap id (bogus; we only have one heap).
1597 JDWP::Write1BE(&p, 8); // Size of allocation unit, in bytes.
1598
1599 JDWP::Write4BE(&p, reinterpret_cast<uintptr_t>(chunk_ptr)); // virtual address of segment start.
1600 JDWP::Write4BE(&p, 0); // offset of this piece (relative to the virtual address).
1601 // [u4]: length of piece, in allocation units
1602 // We won't know this until we're done, so save the offset and stuff in a dummy value.
1603 pieceLenField = p;
1604 JDWP::Write4BE(&p, 0x55555555);
1605 needHeader = false;
1606 }
1607
1608 void Flush() {
1609 // Patch the "length of piece" field.
1610 CHECK_LE(&buf[0], pieceLenField);
1611 CHECK_LE(pieceLenField, p);
1612 JDWP::Set4BE(pieceLenField, totalAllocationUnits);
1613
1614 Dbg::DdmSendChunk(type, p - &buf[0], &buf[0]);
1615 Reset();
1616 }
1617
Elliott Hughesa2155262011-11-16 16:26:58 -08001618 static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
1619 reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
1620 }
1621
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001622 private:
Elliott Hughesa2155262011-11-16 16:26:58 -08001623 enum { ALLOCATION_UNIT_SIZE = 8 };
1624
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001625 void Reset() {
1626 p = &buf[0];
1627 totalAllocationUnits = 0;
1628 needHeader = true;
1629 pieceLenField = NULL;
1630 }
1631
Elliott Hughesa2155262011-11-16 16:26:58 -08001632 void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
1633 CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001634
Elliott Hughesa2155262011-11-16 16:26:58 -08001635 /* Make sure there's enough room left in the buffer.
1636 * We need to use two bytes for every fractional 256
1637 * allocation units used by the chunk.
1638 */
1639 {
1640 size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
1641 size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1642 if (bytesLeft < needed) {
1643 Flush();
1644 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001645
Elliott Hughesa2155262011-11-16 16:26:58 -08001646 bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1647 if (bytesLeft < needed) {
1648 LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
1649 return;
1650 }
1651 }
1652
1653 // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
1654 EnsureHeader(chunk_ptr);
1655
1656 // Determine the type of this chunk.
1657 // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
1658 // If it's the same, we should combine them.
1659 uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
1660
1661 // Write out the chunk description.
1662 chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
1663 totalAllocationUnits += chunk_len;
1664 while (chunk_len > 256) {
1665 *p++ = state | HPSG_PARTIAL;
1666 *p++ = 255; // length - 1
1667 chunk_len -= 256;
1668 }
1669 *p++ = state;
1670 *p++ = chunk_len - 1;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001671 }
1672
Elliott Hughesa2155262011-11-16 16:26:58 -08001673 uint8_t ExamineObject(const Object* o, bool is_native_heap) {
1674 if (o == NULL) {
1675 return HPSG_STATE(SOLIDITY_FREE, 0);
1676 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001677
Elliott Hughesa2155262011-11-16 16:26:58 -08001678 // It's an allocated chunk. Figure out what it is.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001679
Elliott Hughesa2155262011-11-16 16:26:58 -08001680 // If we're looking at the native heap, we'll just return
1681 // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
1682 if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
1683 return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
1684 }
1685
1686 Class* c = o->GetClass();
1687 if (c == NULL) {
1688 // The object was probably just created but hasn't been initialized yet.
1689 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1690 }
1691
1692 if (!Heap::IsHeapAddress(c)) {
1693 LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
1694 return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
1695 }
1696
1697 if (c->IsClassClass()) {
1698 return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
1699 }
1700
1701 if (c->IsArrayClass()) {
1702 if (o->IsObjectArray()) {
1703 return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1704 }
1705 switch (c->GetComponentSize()) {
1706 case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
1707 case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
1708 case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1709 case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
1710 }
1711 }
1712
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001713 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1714 }
1715
Elliott Hughesa2155262011-11-16 16:26:58 -08001716 DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
1717};
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001718
1719void Dbg::DdmSendHeapSegments(bool native) {
1720 Dbg::HpsgWhen when;
1721 Dbg::HpsgWhat what;
1722 if (!native) {
1723 when = gDdmHpsgWhen;
1724 what = gDdmHpsgWhat;
1725 } else {
1726 when = gDdmNhsgWhen;
1727 what = gDdmNhsgWhat;
1728 }
1729 if (when == HPSG_WHEN_NEVER) {
1730 return;
1731 }
1732
1733 // Figure out what kind of chunks we'll be sending.
1734 CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
1735
1736 // First, send a heap start chunk.
1737 uint8_t heap_id[4];
1738 JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
1739 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
1740
1741 // Send a series of heap segment chunks.
Elliott Hughesa2155262011-11-16 16:26:58 -08001742 HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
1743 if (native) {
1744 dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
1745 } else {
1746 Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
1747 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001748
1749 // Finally, send a heap end chunk.
1750 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
Elliott Hughes767a1472011-10-26 18:49:02 -07001751}
1752
Elliott Hughes545a0642011-11-08 19:10:03 -08001753void Dbg::SetAllocTrackingEnabled(bool enabled) {
1754 MutexLock mu(gAllocTrackerLock);
1755 if (enabled) {
1756 if (recent_allocation_records_ == NULL) {
1757 LOG(INFO) << "Enabling alloc tracker (" << kNumAllocRecords << " entries, "
1758 << kMaxAllocRecordStackDepth << " frames --> "
1759 << (sizeof(AllocRecord) * kNumAllocRecords) << " bytes)";
1760 gAllocRecordHead = gAllocRecordCount = 0;
1761 recent_allocation_records_ = new AllocRecord[kNumAllocRecords];
1762 CHECK(recent_allocation_records_ != NULL);
1763 }
1764 } else {
1765 delete[] recent_allocation_records_;
1766 recent_allocation_records_ = NULL;
1767 }
1768}
1769
1770struct AllocRecordStackVisitor : public Thread::StackVisitor {
1771 AllocRecordStackVisitor(AllocRecord* record) : record(record), depth(0) {
1772 }
1773
1774 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
1775 if (depth >= kMaxAllocRecordStackDepth) {
1776 return;
1777 }
1778 Method* m = f.GetMethod();
1779 if (m == NULL || m->IsCalleeSaveMethod()) {
1780 return;
1781 }
1782 record->stack[depth].method = m;
1783 record->stack[depth].raw_pc = pc;
1784 ++depth;
1785 }
1786
1787 ~AllocRecordStackVisitor() {
1788 // Clear out any unused stack trace elements.
1789 for (; depth < kMaxAllocRecordStackDepth; ++depth) {
1790 record->stack[depth].method = NULL;
1791 record->stack[depth].raw_pc = 0;
1792 }
1793 }
1794
1795 AllocRecord* record;
1796 size_t depth;
1797};
1798
1799void Dbg::RecordAllocation(Class* type, size_t byte_count) {
1800 Thread* self = Thread::Current();
1801 CHECK(self != NULL);
1802
1803 MutexLock mu(gAllocTrackerLock);
1804 if (recent_allocation_records_ == NULL) {
1805 return;
1806 }
1807
1808 // Advance and clip.
1809 if (++gAllocRecordHead == kNumAllocRecords) {
1810 gAllocRecordHead = 0;
1811 }
1812
1813 // Fill in the basics.
1814 AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
1815 record->type = type;
1816 record->byte_count = byte_count;
1817 record->thin_lock_id = self->GetThinLockId();
1818
1819 // Fill in the stack trace.
1820 AllocRecordStackVisitor visitor(record);
1821 self->WalkStack(&visitor);
1822
1823 if (gAllocRecordCount < kNumAllocRecords) {
1824 ++gAllocRecordCount;
1825 }
1826}
1827
1828/*
1829 * Return the index of the head element.
1830 *
1831 * We point at the most-recently-written record, so if allocRecordCount is 1
1832 * we want to use the current element. Take "head+1" and subtract count
1833 * from it.
1834 *
1835 * We need to handle underflow in our circular buffer, so we add
1836 * kNumAllocRecords and then mask it back down.
1837 */
1838inline static int headIndex() {
1839 return (gAllocRecordHead+1 + kNumAllocRecords - gAllocRecordCount) & (kNumAllocRecords-1);
1840}
1841
1842void Dbg::DumpRecentAllocations() {
1843 MutexLock mu(gAllocTrackerLock);
1844 if (recent_allocation_records_ == NULL) {
1845 LOG(INFO) << "Not recording tracked allocations";
1846 return;
1847 }
1848
1849 // "i" is the head of the list. We want to start at the end of the
1850 // list and move forward to the tail.
1851 size_t i = headIndex();
1852 size_t count = gAllocRecordCount;
1853
1854 LOG(INFO) << "Tracked allocations, (head=" << gAllocRecordHead << " count=" << count << ")";
1855 while (count--) {
1856 AllocRecord* record = &recent_allocation_records_[i];
1857
1858 LOG(INFO) << StringPrintf(" T=%-2d %6d ", record->thin_lock_id, record->byte_count)
1859 << PrettyClass(record->type);
1860
1861 for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
1862 const Method* m = record->stack[stack_frame].method;
1863 if (m == NULL) {
1864 break;
1865 }
1866 LOG(INFO) << " " << PrettyMethod(m) << " line " << record->stack[stack_frame].LineNumber();
1867 }
1868
1869 // pause periodically to help logcat catch up
1870 if ((count % 5) == 0) {
1871 usleep(40000);
1872 }
1873
1874 i = (i + 1) & (kNumAllocRecords-1);
1875 }
1876}
1877
1878class StringTable {
1879 public:
1880 StringTable() {
1881 }
1882
1883 void Add(const String* s) {
1884 table_.insert(s);
1885 }
1886
1887 size_t IndexOf(const String* s) {
1888 return std::distance(table_.begin(), table_.find(s));
1889 }
1890
1891 size_t Size() {
1892 return table_.size();
1893 }
1894
1895 void WriteTo(std::vector<uint8_t>& bytes) {
1896 typedef std::set<const String*>::const_iterator It; // TODO: C++0x auto
1897 for (It it = table_.begin(); it != table_.end(); ++it) {
1898 const String* s = *it;
1899 JDWP::AppendUtf16BE(bytes, s->GetCharArray()->GetData(), s->GetLength());
1900 }
1901 }
1902
1903 private:
1904 std::set<const String*> table_;
1905 DISALLOW_COPY_AND_ASSIGN(StringTable);
1906};
1907
1908/*
1909 * The data we send to DDMS contains everything we have recorded.
1910 *
1911 * Message header (all values big-endian):
1912 * (1b) message header len (to allow future expansion); includes itself
1913 * (1b) entry header len
1914 * (1b) stack frame len
1915 * (2b) number of entries
1916 * (4b) offset to string table from start of message
1917 * (2b) number of class name strings
1918 * (2b) number of method name strings
1919 * (2b) number of source file name strings
1920 * For each entry:
1921 * (4b) total allocation size
1922 * (2b) threadId
1923 * (2b) allocated object's class name index
1924 * (1b) stack depth
1925 * For each stack frame:
1926 * (2b) method's class name
1927 * (2b) method name
1928 * (2b) method source file
1929 * (2b) line number, clipped to 32767; -2 if native; -1 if no source
1930 * (xb) class name strings
1931 * (xb) method name strings
1932 * (xb) source file strings
1933 *
1934 * As with other DDM traffic, strings are sent as a 4-byte length
1935 * followed by UTF-16 data.
1936 *
1937 * We send up 16-bit unsigned indexes into string tables. In theory there
1938 * can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
1939 * each table, but in practice there should be far fewer.
1940 *
1941 * The chief reason for using a string table here is to keep the size of
1942 * the DDMS message to a minimum. This is partly to make the protocol
1943 * efficient, but also because we have to form the whole thing up all at
1944 * once in a memory buffer.
1945 *
1946 * We use separate string tables for class names, method names, and source
1947 * files to keep the indexes small. There will generally be no overlap
1948 * between the contents of these tables.
1949 */
1950jbyteArray Dbg::GetRecentAllocations() {
1951 if (false) {
1952 DumpRecentAllocations();
1953 }
1954
1955 MutexLock mu(gAllocTrackerLock);
1956
1957 /*
1958 * Part 1: generate string tables.
1959 */
1960 StringTable class_names;
1961 StringTable method_names;
1962 StringTable filenames;
1963
1964 int count = gAllocRecordCount;
1965 int idx = headIndex();
1966 while (count--) {
1967 AllocRecord* record = &recent_allocation_records_[idx];
1968
1969 class_names.Add(record->type->GetDescriptor());
1970
1971 for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
1972 const Method* m = record->stack[i].method;
1973 if (m != NULL) {
1974 class_names.Add(m->GetDeclaringClass()->GetDescriptor());
1975 method_names.Add(m->GetName());
1976 filenames.Add(m->GetDeclaringClass()->GetSourceFile());
1977 }
1978 }
1979
1980 idx = (idx + 1) & (kNumAllocRecords-1);
1981 }
1982
1983 LOG(INFO) << "allocation records: " << gAllocRecordCount;
1984
1985 /*
1986 * Part 2: allocate a buffer and generate the output.
1987 */
1988 std::vector<uint8_t> bytes;
1989
1990 // (1b) message header len (to allow future expansion); includes itself
1991 // (1b) entry header len
1992 // (1b) stack frame len
1993 const int kMessageHeaderLen = 15;
1994 const int kEntryHeaderLen = 9;
1995 const int kStackFrameLen = 8;
1996 JDWP::Append1BE(bytes, kMessageHeaderLen);
1997 JDWP::Append1BE(bytes, kEntryHeaderLen);
1998 JDWP::Append1BE(bytes, kStackFrameLen);
1999
2000 // (2b) number of entries
2001 // (4b) offset to string table from start of message
2002 // (2b) number of class name strings
2003 // (2b) number of method name strings
2004 // (2b) number of source file name strings
2005 JDWP::Append2BE(bytes, gAllocRecordCount);
2006 size_t string_table_offset = bytes.size();
2007 JDWP::Append4BE(bytes, 0); // We'll patch this later...
2008 JDWP::Append2BE(bytes, class_names.Size());
2009 JDWP::Append2BE(bytes, method_names.Size());
2010 JDWP::Append2BE(bytes, filenames.Size());
2011
2012 count = gAllocRecordCount;
2013 idx = headIndex();
2014 while (count--) {
2015 // For each entry:
2016 // (4b) total allocation size
2017 // (2b) thread id
2018 // (2b) allocated object's class name index
2019 // (1b) stack depth
2020 AllocRecord* record = &recent_allocation_records_[idx];
2021 size_t stack_depth = record->GetDepth();
2022 JDWP::Append4BE(bytes, record->byte_count);
2023 JDWP::Append2BE(bytes, record->thin_lock_id);
2024 JDWP::Append2BE(bytes, class_names.IndexOf(record->type->GetDescriptor()));
2025 JDWP::Append1BE(bytes, stack_depth);
2026
2027 for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
2028 // For each stack frame:
2029 // (2b) method's class name
2030 // (2b) method name
2031 // (2b) method source file
2032 // (2b) line number, clipped to 32767; -2 if native; -1 if no source
2033 const Method* m = record->stack[stack_frame].method;
2034 JDWP::Append2BE(bytes, class_names.IndexOf(m->GetDeclaringClass()->GetDescriptor()));
2035 JDWP::Append2BE(bytes, method_names.IndexOf(m->GetName()));
2036 JDWP::Append2BE(bytes, filenames.IndexOf(m->GetDeclaringClass()->GetSourceFile()));
2037 JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
2038 }
2039
2040 idx = (idx + 1) & (kNumAllocRecords-1);
2041 }
2042
2043 // (xb) class name strings
2044 // (xb) method name strings
2045 // (xb) source file strings
2046 JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
2047 class_names.WriteTo(bytes);
2048 method_names.WriteTo(bytes);
2049 filenames.WriteTo(bytes);
2050
2051 JNIEnv* env = Thread::Current()->GetJniEnv();
2052 jbyteArray result = env->NewByteArray(bytes.size());
2053 if (result != NULL) {
2054 env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
2055 }
2056 return result;
2057}
2058
Elliott Hughes872d4ec2011-10-21 17:07:15 -07002059} // namespace art