blob: 976fb89ad6db6ffd361c4b06ddfe0055c7139df8 [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 Hughes68fdbd02011-11-29 19:22:47 -080024#include "context.h"
Elliott Hughes6a5bd492011-10-28 14:33:57 -070025#include "ScopedLocalRef.h"
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -070026#include "ScopedPrimitiveArray.h"
Elliott Hughes47fce012011-10-25 18:37:19 -070027#include "stack_indirect_reference_table.h"
Elliott Hughes475fc232011-10-25 15:00:35 -070028#include "thread_list.h"
29
Elliott Hughes6a5bd492011-10-28 14:33:57 -070030extern "C" void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*);
31#ifndef HAVE_ANDROID_OS
32void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*) {
33 // No-op for glibc.
34}
35#endif
36
Elliott Hughes872d4ec2011-10-21 17:07:15 -070037namespace art {
38
Elliott Hughes545a0642011-11-08 19:10:03 -080039static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
40static const size_t kNumAllocRecords = 512; // Must be power of 2.
41
Elliott Hughes475fc232011-10-25 15:00:35 -070042class ObjectRegistry {
43 public:
44 ObjectRegistry() : lock_("ObjectRegistry lock") {
45 }
46
47 JDWP::ObjectId Add(Object* o) {
48 if (o == NULL) {
49 return 0;
50 }
51 JDWP::ObjectId id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(o));
52 MutexLock mu(lock_);
53 map_[id] = o;
54 return id;
55 }
56
Elliott Hughes234ab152011-10-26 14:02:26 -070057 void Clear() {
58 MutexLock mu(lock_);
59 LOG(DEBUG) << "Debugger has detached; object registry had " << map_.size() << " entries";
60 map_.clear();
61 }
62
Elliott Hughes475fc232011-10-25 15:00:35 -070063 bool Contains(JDWP::ObjectId id) {
64 MutexLock mu(lock_);
65 return map_.find(id) != map_.end();
66 }
67
Elliott Hughesa2155262011-11-16 16:26:58 -080068 template<typename T> T Get(JDWP::ObjectId id) {
69 MutexLock mu(lock_);
70 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
71 It it = map_.find(id);
72 return (it != map_.end()) ? reinterpret_cast<T>(it->second) : NULL;
73 }
74
Elliott Hughesbfe487b2011-10-26 15:48:55 -070075 void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
76 MutexLock mu(lock_);
77 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
78 for (It it = map_.begin(); it != map_.end(); ++it) {
79 visitor(it->second, arg);
80 }
81 }
82
Elliott Hughes475fc232011-10-25 15:00:35 -070083 private:
84 Mutex lock_;
85 std::map<JDWP::ObjectId, Object*> map_;
86};
87
Elliott Hughes545a0642011-11-08 19:10:03 -080088struct AllocRecordStackTraceElement {
89 const Method* method;
90 uintptr_t raw_pc;
91
92 int32_t LineNumber() const {
93 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
94 Class* c = method->GetDeclaringClass();
95 DexCache* dex_cache = c->GetDexCache();
96 const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
97 return dex_file.GetLineNumFromPC(method, method->ToDexPC(raw_pc));
98 }
99};
100
101struct AllocRecord {
102 Class* type;
103 size_t byte_count;
104 uint16_t thin_lock_id;
105 AllocRecordStackTraceElement stack[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
106
107 size_t GetDepth() {
108 size_t depth = 0;
109 while (depth < kMaxAllocRecordStackDepth && stack[depth].method != NULL) {
110 ++depth;
111 }
112 return depth;
113 }
114};
115
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700116// JDWP is allowed unless the Zygote forbids it.
117static bool gJdwpAllowed = true;
118
Elliott Hughes3bb81562011-10-21 18:52:59 -0700119// Was there a -Xrunjdwp or -agent argument on the command-line?
120static bool gJdwpConfigured = false;
121
122// Broken-down JDWP options. (Only valid if gJdwpConfigured is true.)
Elliott Hughes376a7a02011-10-24 18:35:55 -0700123static JDWP::JdwpOptions gJdwpOptions;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700124
125// Runtime JDWP state.
126static JDWP::JdwpState* gJdwpState = NULL;
127static bool gDebuggerConnected; // debugger or DDMS is connected.
128static bool gDebuggerActive; // debugger is making requests.
129
Elliott Hughes47fce012011-10-25 18:37:19 -0700130static bool gDdmThreadNotification = false;
131
Elliott Hughes767a1472011-10-26 18:49:02 -0700132// DDMS GC-related settings.
133static Dbg::HpifWhen gDdmHpifWhen = Dbg::HPIF_WHEN_NEVER;
134static Dbg::HpsgWhen gDdmHpsgWhen = Dbg::HPSG_WHEN_NEVER;
135static Dbg::HpsgWhat gDdmHpsgWhat;
136static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
137static Dbg::HpsgWhat gDdmNhsgWhat;
138
Elliott Hughes475fc232011-10-25 15:00:35 -0700139static ObjectRegistry* gRegistry = NULL;
140
Elliott Hughes545a0642011-11-08 19:10:03 -0800141// Recent allocation tracking.
142static Mutex gAllocTrackerLock("AllocTracker lock");
143AllocRecord* Dbg::recent_allocation_records_ = NULL; // TODO: CircularBuffer<AllocRecord>
144static size_t gAllocRecordHead = 0;
145static size_t gAllocRecordCount = 0;
146
Elliott Hughes3bb81562011-10-21 18:52:59 -0700147/*
148 * Handle one of the JDWP name/value pairs.
149 *
150 * JDWP options are:
151 * help: if specified, show help message and bail
152 * transport: may be dt_socket or dt_shmem
153 * address: for dt_socket, "host:port", or just "port" when listening
154 * server: if "y", wait for debugger to attach; if "n", attach to debugger
155 * timeout: how long to wait for debugger to connect / listen
156 *
157 * Useful with server=n (these aren't supported yet):
158 * onthrow=<exception-name>: connect to debugger when exception thrown
159 * onuncaught=y|n: connect to debugger when uncaught exception thrown
160 * launch=<command-line>: launch the debugger itself
161 *
162 * The "transport" option is required, as is "address" if server=n.
163 */
164static bool ParseJdwpOption(const std::string& name, const std::string& value) {
165 if (name == "transport") {
166 if (value == "dt_socket") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700167 gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700168 } else if (value == "dt_android_adb") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700169 gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700170 } else {
171 LOG(ERROR) << "JDWP transport not supported: " << value;
172 return false;
173 }
174 } else if (name == "server") {
175 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700176 gJdwpOptions.server = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700177 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700178 gJdwpOptions.server = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700179 } else {
180 LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
181 return false;
182 }
183 } else if (name == "suspend") {
184 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700185 gJdwpOptions.suspend = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700186 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700187 gJdwpOptions.suspend = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700188 } else {
189 LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
190 return false;
191 }
192 } else if (name == "address") {
193 /* this is either <port> or <host>:<port> */
194 std::string port_string;
Elliott Hughes376a7a02011-10-24 18:35:55 -0700195 gJdwpOptions.host.clear();
Elliott Hughes3bb81562011-10-21 18:52:59 -0700196 std::string::size_type colon = value.find(':');
197 if (colon != std::string::npos) {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700198 gJdwpOptions.host = value.substr(0, colon);
Elliott Hughes3bb81562011-10-21 18:52:59 -0700199 port_string = value.substr(colon + 1);
200 } else {
201 port_string = value;
202 }
203 if (port_string.empty()) {
204 LOG(ERROR) << "JDWP address missing port: " << value;
205 return false;
206 }
207 char* end;
208 long port = strtol(port_string.c_str(), &end, 10);
209 if (*end != '\0') {
210 LOG(ERROR) << "JDWP address has junk in port field: " << value;
211 return false;
212 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700213 gJdwpOptions.port = port;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700214 } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
215 /* valid but unsupported */
216 LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
217 } else {
218 LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
219 }
220
221 return true;
222}
223
224/*
225 * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
226 * "transport=dt_socket,address=8000,server=y,suspend=n"
227 */
228bool Dbg::ParseJdwpOptions(const std::string& options) {
Elliott Hughes47fce012011-10-25 18:37:19 -0700229 LOG(VERBOSE) << "ParseJdwpOptions: " << options;
230
Elliott Hughes3bb81562011-10-21 18:52:59 -0700231 std::vector<std::string> pairs;
232 Split(options, ',', pairs);
233
234 for (size_t i = 0; i < pairs.size(); ++i) {
235 std::string::size_type equals = pairs[i].find('=');
236 if (equals == std::string::npos) {
237 LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
238 return false;
239 }
240 ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
241 }
242
Elliott Hughes376a7a02011-10-24 18:35:55 -0700243 if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700244 LOG(ERROR) << "Must specify JDWP transport: " << options;
245 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700246 if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700247 LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
248 return false;
249 }
250
251 gJdwpConfigured = true;
252 return true;
253}
254
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700255void Dbg::StartJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700256 if (!gJdwpAllowed || !gJdwpConfigured) {
257 // No JDWP for you!
258 return;
259 }
260
Elliott Hughes475fc232011-10-25 15:00:35 -0700261 CHECK(gRegistry == NULL);
262 gRegistry = new ObjectRegistry;
263
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700264 // Init JDWP if the debugger is enabled. This may connect out to a
265 // debugger, passively listen for a debugger, or block waiting for a
266 // debugger.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700267 gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
268 if (gJdwpState == NULL) {
269 LOG(WARNING) << "debugger thread failed to initialize";
Elliott Hughes475fc232011-10-25 15:00:35 -0700270 return;
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700271 }
272
273 // If a debugger has already attached, send the "welcome" message.
274 // This may cause us to suspend all threads.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700275 if (gJdwpState->IsActive()) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800276 //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Elliott Hughes376a7a02011-10-24 18:35:55 -0700277 if (!gJdwpState->PostVMStart()) {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700278 LOG(WARNING) << "failed to post 'start' message to debugger";
279 }
280 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700281}
282
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700283void Dbg::StopJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700284 delete gJdwpState;
Elliott Hughes475fc232011-10-25 15:00:35 -0700285 delete gRegistry;
286 gRegistry = NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700287}
288
Elliott Hughes767a1472011-10-26 18:49:02 -0700289void Dbg::GcDidFinish() {
290 if (gDdmHpifWhen != HPIF_WHEN_NEVER) {
291 LOG(DEBUG) << "Sending VM heap info to DDM";
Elliott Hughes7162ad92011-10-27 14:08:42 -0700292 DdmSendHeapInfo(gDdmHpifWhen);
Elliott Hughes767a1472011-10-26 18:49:02 -0700293 }
294 if (gDdmHpsgWhen != HPSG_WHEN_NEVER) {
295 LOG(DEBUG) << "Dumping VM heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700296 DdmSendHeapSegments(false);
Elliott Hughes767a1472011-10-26 18:49:02 -0700297 }
298 if (gDdmNhsgWhen != HPSG_WHEN_NEVER) {
299 LOG(DEBUG) << "Dumping native heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700300 DdmSendHeapSegments(true);
Elliott Hughes767a1472011-10-26 18:49:02 -0700301 }
302}
303
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700304void Dbg::SetJdwpAllowed(bool allowed) {
305 gJdwpAllowed = allowed;
306}
307
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700308DebugInvokeReq* Dbg::GetInvokeReq() {
Elliott Hughes475fc232011-10-25 15:00:35 -0700309 return Thread::Current()->GetInvokeReq();
310}
311
312Thread* Dbg::GetDebugThread() {
313 return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
314}
315
316void Dbg::ClearWaitForEventThread() {
317 gJdwpState->ClearWaitForEventThread();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700318}
319
320void Dbg::Connected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700321 CHECK(!gDebuggerConnected);
322 LOG(VERBOSE) << "JDWP has attached";
323 gDebuggerConnected = true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700324}
325
Elliott Hughesa2155262011-11-16 16:26:58 -0800326void Dbg::GoActive() {
327 // Enable all debugging features, including scans for breakpoints.
328 // This is a no-op if we're already active.
329 // Only called from the JDWP handler thread.
330 if (gDebuggerActive) {
331 return;
332 }
333
334 LOG(INFO) << "Debugger is active";
335
336 // TODO: CHECK we don't have any outstanding breakpoints.
337
338 gDebuggerActive = true;
339
340 //dvmEnableAllSubMode(kSubModeDebuggerActive);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700341}
342
343void Dbg::Disconnected() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700344 CHECK(gDebuggerConnected);
345
346 gDebuggerActive = false;
347
348 //dvmDisableAllSubMode(kSubModeDebuggerActive);
349
350 gRegistry->Clear();
351 gDebuggerConnected = false;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700352}
353
354bool Dbg::IsDebuggerConnected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700355 return gDebuggerActive;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700356}
357
358bool Dbg::IsDebuggingEnabled() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700359 return gJdwpConfigured;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700360}
361
362int64_t Dbg::LastDebuggerActivity() {
363 UNIMPLEMENTED(WARNING);
364 return -1;
365}
366
367int Dbg::ThreadRunning() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700368 return static_cast<int>(Thread::Current()->SetState(Thread::kRunnable));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700369}
370
371int Dbg::ThreadWaiting() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700372 return static_cast<int>(Thread::Current()->SetState(Thread::kVmWait));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700373}
374
Elliott Hughes6ba581a2011-10-25 11:45:35 -0700375int Dbg::ThreadContinuing(int new_state) {
376 return static_cast<int>(Thread::Current()->SetState(static_cast<Thread::State>(new_state)));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700377}
378
379void Dbg::UndoDebuggerSuspensions() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700380 Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700381}
382
383void Dbg::Exit(int status) {
384 UNIMPLEMENTED(FATAL);
385}
386
Elliott Hughesbfe487b2011-10-26 15:48:55 -0700387void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
388 if (gRegistry != NULL) {
389 gRegistry->VisitRoots(visitor, arg);
390 }
391}
392
Elliott Hughesa2155262011-11-16 16:26:58 -0800393std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
394 Class* c = gRegistry->Get<Class*>(classId);
395 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700396}
397
398JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
399 UNIMPLEMENTED(FATAL);
400 return 0;
401}
402
403JDWP::RefTypeId Dbg::GetSuperclass(JDWP::RefTypeId id) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800404 Class* c = gRegistry->Get<Class*>(id);
405 return gRegistry->Add(c->GetSuperClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700406}
407
408JDWP::ObjectId Dbg::GetClassLoader(JDWP::RefTypeId id) {
409 UNIMPLEMENTED(FATAL);
410 return 0;
411}
412
413uint32_t Dbg::GetAccessFlags(JDWP::RefTypeId id) {
414 UNIMPLEMENTED(FATAL);
415 return 0;
416}
417
418bool Dbg::IsInterface(JDWP::RefTypeId id) {
419 UNIMPLEMENTED(FATAL);
420 return false;
421}
422
Elliott Hughesa2155262011-11-16 16:26:58 -0800423void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
424 // Get the complete list of reference classes (i.e. all classes except
425 // the primitive types).
426 // Returns a newly-allocated buffer full of RefTypeId values.
427 struct ClassListCreator {
428 static bool Visit(Class* c, void* arg) {
429 return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
430 }
431
432 bool Visit(Class* c) {
433 if (!c->IsPrimitive()) {
434 classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
435 }
436 return true;
437 }
438
439 std::vector<JDWP::RefTypeId> classes;
440 };
441
442 ClassListCreator clc;
443 Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
444 *pClassCount = clc.classes.size();
445 *pClasses = new JDWP::RefTypeId[clc.classes.size()];
446 for (size_t i = 0; i < clc.classes.size(); ++i) {
447 (*pClasses)[i] = clc.classes[i];
448 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700449}
450
451void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
452 UNIMPLEMENTED(FATAL);
453}
454
Elliott Hughesa2155262011-11-16 16:26:58 -0800455void Dbg::GetClassInfo(JDWP::RefTypeId classId, uint8_t* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
456 Class* c = gRegistry->Get<Class*>(classId);
457 if (c->IsArrayClass()) {
458 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
459 *pTypeTag = JDWP::TT_ARRAY;
460 } else {
461 if (c->IsErroneous()) {
462 *pStatus = JDWP::CS_ERROR;
463 } else {
464 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
465 }
466 *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
467 }
468
469 if (pDescriptor != NULL) {
470 *pDescriptor = c->GetDescriptor()->ToModifiedUtf8();
471 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700472}
473
474bool Dbg::FindLoadedClassBySignature(const char* classDescriptor, JDWP::RefTypeId* pRefTypeId) {
475 UNIMPLEMENTED(FATAL);
476 return false;
477}
478
479void Dbg::GetObjectType(JDWP::ObjectId objectId, uint8_t* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800480 Object* o = gRegistry->Get<Object*>(objectId);
481 if (o->GetClass()->IsArrayClass()) {
482 *pRefTypeTag = JDWP::TT_ARRAY;
483 } else if (o->GetClass()->IsInterface()) {
484 *pRefTypeTag = JDWP::TT_INTERFACE;
485 } else {
486 *pRefTypeTag = JDWP::TT_CLASS;
487 }
488 *pRefTypeId = gRegistry->Add(o->GetClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700489}
490
491uint8_t Dbg::GetClassObjectType(JDWP::RefTypeId refTypeId) {
492 UNIMPLEMENTED(FATAL);
493 return 0;
494}
495
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800496std::string Dbg::GetSignature(JDWP::RefTypeId refTypeId) {
497 Class* c = gRegistry->Get<Class*>(refTypeId);
498 CHECK(c != NULL);
499 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700500}
501
Elliott Hughes03181a82011-11-17 17:22:21 -0800502bool Dbg::GetSourceFile(JDWP::RefTypeId refTypeId, std::string& result) {
503 Class* c = gRegistry->Get<Class*>(refTypeId);
504 CHECK(c != NULL);
505
506 String* source_file = c->GetSourceFile();
507 if (source_file == NULL) {
508 return false;
509 }
510 result = source_file->ToModifiedUtf8();
511 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700512}
513
514const char* Dbg::GetObjectTypeName(JDWP::ObjectId objectId) {
515 UNIMPLEMENTED(FATAL);
516 return NULL;
517}
518
519uint8_t Dbg::GetObjectTag(JDWP::ObjectId objectId) {
520 UNIMPLEMENTED(FATAL);
521 return 0;
522}
523
Elliott Hughesdbb40792011-11-18 17:05:22 -0800524size_t Dbg::GetTagWidth(int tag) {
525 switch (tag) {
526 case JDWP::JT_VOID:
527 return 0;
528 case JDWP::JT_BYTE:
529 case JDWP::JT_BOOLEAN:
530 return 1;
531 case JDWP::JT_CHAR:
532 case JDWP::JT_SHORT:
533 return 2;
534 case JDWP::JT_FLOAT:
535 case JDWP::JT_INT:
536 return 4;
537 case JDWP::JT_ARRAY:
538 case JDWP::JT_OBJECT:
539 case JDWP::JT_STRING:
540 case JDWP::JT_THREAD:
541 case JDWP::JT_THREAD_GROUP:
542 case JDWP::JT_CLASS_LOADER:
543 case JDWP::JT_CLASS_OBJECT:
544 return sizeof(JDWP::ObjectId);
545 case JDWP::JT_DOUBLE:
546 case JDWP::JT_LONG:
547 return 8;
548 default:
549 LOG(FATAL) << "unknown tag " << tag;
550 return -1;
551 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700552}
553
554int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800555 Object* o = gRegistry->Get<Object*>(arrayId);
556 Array* a = o->AsArray();
557 return a->GetLength();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700558}
559
560uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
561 UNIMPLEMENTED(FATAL);
562 return 0;
563}
564
565bool Dbg::OutputArray(JDWP::ObjectId arrayId, int firstIndex, int count, JDWP::ExpandBuf* pReply) {
566 UNIMPLEMENTED(FATAL);
567 return false;
568}
569
570bool Dbg::SetArrayElements(JDWP::ObjectId arrayId, int firstIndex, int count, const uint8_t* buf) {
571 UNIMPLEMENTED(FATAL);
572 return false;
573}
574
575JDWP::ObjectId Dbg::CreateString(const char* str) {
576 UNIMPLEMENTED(FATAL);
577 return 0;
578}
579
580JDWP::ObjectId Dbg::CreateObject(JDWP::RefTypeId classId) {
581 UNIMPLEMENTED(FATAL);
582 return 0;
583}
584
585JDWP::ObjectId Dbg::CreateArrayObject(JDWP::RefTypeId arrayTypeId, uint32_t length) {
586 UNIMPLEMENTED(FATAL);
587 return 0;
588}
589
590bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
591 UNIMPLEMENTED(FATAL);
592 return false;
593}
594
Elliott Hughes03181a82011-11-17 17:22:21 -0800595JDWP::FieldId ToFieldId(Field* f) {
596#ifdef MOVING_GARBAGE_COLLECTOR
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700597 UNIMPLEMENTED(FATAL);
Elliott Hughes03181a82011-11-17 17:22:21 -0800598#else
599 return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
600#endif
601}
602
603JDWP::MethodId ToMethodId(Method* m) {
604#ifdef MOVING_GARBAGE_COLLECTOR
605 UNIMPLEMENTED(FATAL);
606#else
607 return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
608#endif
609}
610
611Method* FromMethodId(JDWP::MethodId mid) {
612#ifdef MOVING_GARBAGE_COLLECTOR
613 UNIMPLEMENTED(FATAL);
614#else
615 return reinterpret_cast<Method*>(static_cast<uintptr_t>(mid));
616#endif
617}
618
619std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
620 return FromMethodId(methodId)->GetName()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700621}
622
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800623/*
624 * Augment the access flags for synthetic methods and fields by setting
625 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
626 * flags not specified by the Java programming language.
627 */
628static uint32_t MangleAccessFlags(uint32_t accessFlags) {
629 accessFlags &= kAccJavaFlagsMask;
630 if ((accessFlags & kAccSynthetic) != 0) {
631 accessFlags |= 0xf0000000;
632 }
633 return accessFlags;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700634}
635
Elliott Hughesdbb40792011-11-18 17:05:22 -0800636static JDWP::JdwpTag TagFromClass(Class* c) {
637 if (c->IsArrayClass()) {
638 return JDWP::JT_ARRAY;
639 }
640
641 if (c->IsStringClass()) {
642 return JDWP::JT_STRING;
643 } else if (c->IsClassClass()) {
644 return JDWP::JT_CLASS_OBJECT;
645#if 0 // TODO
646 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
647 return JDWP::JT_THREAD;
648 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
649 return JDWP::JT_THREAD_GROUP;
650 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
651 return JDWP::JT_CLASS_LOADER;
652#endif
653 } else {
654 return JDWP::JT_OBJECT;
655 }
656}
657
658/*
659 * Objects declared to hold Object might actually hold a more specific
660 * type. The debugger may take a special interest in these (e.g. it
661 * wants to display the contents of Strings), so we want to return an
662 * appropriate tag.
663 *
664 * Null objects are tagged JT_OBJECT.
665 */
666static JDWP::JdwpTag TagFromObject(const Object* o) {
667 return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
668}
669
670static const uint16_t kEclipseWorkaroundSlot = 1000;
671
672/*
673 * Eclipse appears to expect that the "this" reference is in slot zero.
674 * If it's not, the "variables" display will show two copies of "this",
675 * possibly because it gets "this" from SF.ThisObject and then displays
676 * all locals with nonzero slot numbers.
677 *
678 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
679 * SF.GetValues / SF.SetValues we map them back.
680 */
681static uint16_t MangleSlot(uint16_t slot, const char* name) {
682 uint16_t newSlot = slot;
683 if (strcmp(name, "this") == 0) {
684 newSlot = 0;
685 } else if (slot == 0) {
686 newSlot = kEclipseWorkaroundSlot;
687 }
688 return newSlot;
689}
690
691/*
692 * Reverse Eclipse hack.
693 */
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800694static uint16_t DemangleSlot(uint16_t slot, Frame& f) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800695 if (slot == kEclipseWorkaroundSlot) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800696 return 0;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800697 } else if (slot == 0) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800698 Method* m = f.GetMethod();
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800699 return m->NumRegisters() - m->NumIns();
Elliott Hughesdbb40792011-11-18 17:05:22 -0800700 }
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800701 return slot;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800702}
703
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800704void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
705 Class* c = gRegistry->Get<Class*>(refTypeId);
706 CHECK(c != NULL);
707
708 size_t instance_field_count = c->NumInstanceFields();
709 size_t static_field_count = c->NumStaticFields();
710
711 expandBufAdd4BE(pReply, instance_field_count + static_field_count);
712
713 for (size_t i = 0; i < instance_field_count + static_field_count; ++i) {
714 Field* f = (i < instance_field_count) ? c->GetInstanceField(i) : c->GetStaticField(i - instance_field_count);
715
716 expandBufAddFieldId(pReply, ToFieldId(f));
717 expandBufAddUtf8String(pReply, f->GetName()->ToModifiedUtf8().c_str());
718 expandBufAddUtf8String(pReply, f->GetTypeDescriptor());
719 if (withGeneric) {
720 static const char genericSignature[1] = "";
721 expandBufAddUtf8String(pReply, genericSignature);
722 }
723 expandBufAdd4BE(pReply, MangleAccessFlags(f->GetAccessFlags()));
724 }
725}
726
727void Dbg::OutputDeclaredMethods(JDWP::RefTypeId refTypeId, bool withGeneric, JDWP::ExpandBuf* pReply) {
728 Class* c = gRegistry->Get<Class*>(refTypeId);
729 CHECK(c != NULL);
730
731 size_t direct_method_count = c->NumDirectMethods();
732 size_t virtual_method_count = c->NumVirtualMethods();
733
734 expandBufAdd4BE(pReply, direct_method_count + virtual_method_count);
735
736 for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) {
737 Method* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count);
738
739 expandBufAddMethodId(pReply, ToMethodId(m));
740 expandBufAddUtf8String(pReply, m->GetName()->ToModifiedUtf8().c_str());
741 expandBufAddUtf8String(pReply, m->GetSignature()->ToModifiedUtf8().c_str());
742 if (withGeneric) {
743 static const char genericSignature[1] = "";
744 expandBufAddUtf8String(pReply, genericSignature);
745 }
746 expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags()));
747 }
748}
749
750void Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId refTypeId, JDWP::ExpandBuf* pReply) {
751 Class* c = gRegistry->Get<Class*>(refTypeId);
752 CHECK(c != NULL);
753 size_t interface_count = c->NumInterfaces();
754 expandBufAdd4BE(pReply, interface_count);
755 for (size_t i = 0; i < interface_count; ++i) {
756 expandBufAddRefTypeId(pReply, gRegistry->Add(c->GetInterface(i)));
757 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700758}
759
760void Dbg::OutputLineTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, JDWP::ExpandBuf* pReply) {
Elliott Hughes03181a82011-11-17 17:22:21 -0800761 struct DebugCallbackContext {
762 int numItems;
763 JDWP::ExpandBuf* pReply;
764
765 static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
766 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
767 expandBufAdd8BE(pContext->pReply, address);
768 expandBufAdd4BE(pContext->pReply, lineNum);
769 pContext->numItems++;
770 return true;
771 }
772 };
773
774 Method* m = FromMethodId(methodId);
775 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
776 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
777 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
778
779 uint64_t start, end;
780 if (m->IsNative()) {
781 start = -1;
782 end = -1;
783 } else {
784 start = 0;
785 end = code_item->insns_size_in_code_units_; // TODO: what are the units supposed to be? *2?
786 }
787
788 expandBufAdd8BE(pReply, start);
789 expandBufAdd8BE(pReply, end);
790
791 // Add numLines later
792 size_t numLinesOffset = expandBufGetLength(pReply);
793 expandBufAdd4BE(pReply, 0);
794
795 DebugCallbackContext context;
796 context.numItems = 0;
797 context.pReply = pReply;
798
799 dex_file.DecodeDebugInfo(code_item, m, DebugCallbackContext::Callback, NULL, &context);
800
801 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700802}
803
Elliott Hughesdbb40792011-11-18 17:05:22 -0800804void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, bool withGeneric, JDWP::ExpandBuf* pReply) {
805 struct DebugCallbackContext {
806 int numItems;
807 JDWP::ExpandBuf* pReply;
808 bool withGeneric;
809
810 static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char *name, const char *descriptor, const char *signature) {
811 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
812
Elliott Hughesdbb40792011-11-18 17:05:22 -0800813 LOG(VERBOSE) << StringPrintf(" %2d: %d(%d) '%s' '%s' '%s' slot=%d", pContext->numItems, startAddress, endAddress - startAddress, name, descriptor, signature, slot);
814
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800815 slot = MangleSlot(slot, name);
816
Elliott Hughesdbb40792011-11-18 17:05:22 -0800817 expandBufAdd8BE(pContext->pReply, startAddress);
818 expandBufAddUtf8String(pContext->pReply, name);
819 expandBufAddUtf8String(pContext->pReply, descriptor);
820 if (pContext->withGeneric) {
821 expandBufAddUtf8String(pContext->pReply, signature);
822 }
823 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
824 expandBufAdd4BE(pContext->pReply, slot);
825
826 pContext->numItems++;
827 }
828 };
829
830 Method* m = FromMethodId(methodId);
831 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
832 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
833 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
834
835 expandBufAdd4BE(pReply, m->NumIns());
836
837 // Add numLocals later
838 size_t numLocalsOffset = expandBufGetLength(pReply);
839 expandBufAdd4BE(pReply, 0);
840
841 DebugCallbackContext context;
842 context.numItems = 0;
843 context.pReply = pReply;
844 context.withGeneric = withGeneric;
845
846 dex_file.DecodeDebugInfo(code_item, m, NULL, DebugCallbackContext::Callback, &context);
847
848 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700849}
850
851uint8_t Dbg::GetFieldBasicTag(JDWP::ObjectId objId, JDWP::FieldId fieldId) {
852 UNIMPLEMENTED(FATAL);
853 return 0;
854}
855
856uint8_t Dbg::GetStaticFieldBasicTag(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId) {
857 UNIMPLEMENTED(FATAL);
858 return 0;
859}
860
861void Dbg::GetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
862 UNIMPLEMENTED(FATAL);
863}
864
865void Dbg::SetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, uint64_t value, int width) {
866 UNIMPLEMENTED(FATAL);
867}
868
869void Dbg::GetStaticFieldValue(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
870 UNIMPLEMENTED(FATAL);
871}
872
873void Dbg::SetStaticFieldValue(JDWP::RefTypeId refTypeId, JDWP::FieldId fieldId, uint64_t rawValue, int width) {
874 UNIMPLEMENTED(FATAL);
875}
876
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800877std::string Dbg::StringToUtf8(JDWP::ObjectId strId) {
878 String* s = gRegistry->Get<String*>(strId);
879 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700880}
881
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800882Thread* DecodeThread(JDWP::ObjectId threadId) {
883 Object* thread_peer = gRegistry->Get<Object*>(threadId);
884 CHECK(thread_peer != NULL);
885 return Thread::FromManagedThread(thread_peer);
886}
887
888bool Dbg::GetThreadName(JDWP::ObjectId threadId, std::string& name) {
889 ScopedThreadListLock thread_list_lock;
890 Thread* thread = DecodeThread(threadId);
891 if (thread == NULL) {
892 return false;
893 }
894 StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetName()->ToModifiedUtf8().c_str());
895 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700896}
897
898JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800899 Object* thread = gRegistry->Get<Object*>(threadId);
900 CHECK(thread != NULL);
901
902 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
903 CHECK(c != NULL);
904 Field* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
905 CHECK(f != NULL);
906 Object* group = f->GetObject(thread);
907 CHECK(group != NULL);
908 return gRegistry->Add(group);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700909}
910
Elliott Hughes499c5132011-11-17 14:55:11 -0800911std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
912 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
913 CHECK(thread_group != NULL);
914
915 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
916 CHECK(c != NULL);
917 Field* f = c->FindInstanceField("name", "Ljava/lang/String;");
918 CHECK(f != NULL);
919 String* s = reinterpret_cast<String*>(f->GetObject(thread_group));
920 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700921}
922
923JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
924 UNIMPLEMENTED(FATAL);
925 return 0;
926}
927
Elliott Hughes499c5132011-11-17 14:55:11 -0800928static Object* GetStaticThreadGroup(const char* field_name) {
929 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
930 CHECK(c != NULL);
931 Field* f = c->FindStaticField(field_name, "Ljava/lang/ThreadGroup;");
932 CHECK(f != NULL);
933 Object* group = f->GetObject(NULL);
934 CHECK(group != NULL);
935 return group;
936}
937
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700938JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -0800939 return gRegistry->Add(GetStaticThreadGroup("mSystem"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700940}
941
942JDWP::ObjectId Dbg::GetMainThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -0800943 return gRegistry->Add(GetStaticThreadGroup("mMain"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700944}
945
Elliott Hughes499c5132011-11-17 14:55:11 -0800946bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* pThreadStatus, uint32_t* pSuspendStatus) {
947 ScopedThreadListLock thread_list_lock;
948
949 Thread* thread = DecodeThread(threadId);
950 if (thread == NULL) {
951 return false;
952 }
953
954 switch (thread->GetState()) {
955 case Thread::kTerminated: *pThreadStatus = JDWP::TS_ZOMBIE; break;
956 case Thread::kRunnable: *pThreadStatus = JDWP::TS_RUNNING; break;
957 case Thread::kTimedWaiting: *pThreadStatus = JDWP::TS_SLEEPING; break;
958 case Thread::kBlocked: *pThreadStatus = JDWP::TS_MONITOR; break;
959 case Thread::kWaiting: *pThreadStatus = JDWP::TS_WAIT; break;
960 case Thread::kInitializing: *pThreadStatus = JDWP::TS_ZOMBIE; break;
961 case Thread::kStarting: *pThreadStatus = JDWP::TS_ZOMBIE; break;
962 case Thread::kNative: *pThreadStatus = JDWP::TS_RUNNING; break;
963 case Thread::kVmWait: *pThreadStatus = JDWP::TS_WAIT; break;
964 case Thread::kSuspended: *pThreadStatus = JDWP::TS_RUNNING; break;
965 default:
966 LOG(FATAL) << "unknown thread state " << thread->GetState();
967 }
968
969 *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : 0);
970
971 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700972}
973
974uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
975 UNIMPLEMENTED(FATAL);
976 return 0;
977}
978
979bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -0800980 return DecodeThread(threadId) != NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700981}
982
983bool Dbg::IsSuspended(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -0800984 return DecodeThread(threadId)->IsSuspended();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700985}
986
987//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
988
Elliott Hughesa2155262011-11-16 16:26:58 -0800989void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
990 struct ThreadListVisitor {
991 static void Visit(Thread* t, void* arg) {
992 reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
993 }
994
995 void Visit(Thread* t) {
996 if (t == Dbg::GetDebugThread()) {
997 // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
998 // query all threads, so it's easier if we just don't tell them about this thread.
999 return;
1000 }
1001 if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
1002 threads.push_back(gRegistry->Add(t->GetPeer()));
1003 }
1004 }
1005
1006 Object* thread_group;
1007 std::vector<JDWP::ObjectId> threads;
1008 };
1009
1010 ThreadListVisitor tlv;
1011 tlv.thread_group = thread_group;
1012
1013 {
1014 ScopedThreadListLock thread_list_lock;
1015 Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
1016 }
1017
1018 *pThreadCount = tlv.threads.size();
1019 if (*pThreadCount == 0) {
1020 *ppThreadIds = NULL;
1021 } else {
1022 *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
1023 for (size_t i = 0; i < *pThreadCount; ++i) {
1024 (*ppThreadIds)[i] = tlv.threads[i];
1025 }
1026 }
1027}
1028
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001029void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001030 GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001031}
1032
1033void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001034 GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001035}
1036
1037int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
Elliott Hughes03181a82011-11-17 17:22:21 -08001038 ScopedThreadListLock thread_list_lock;
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001039 struct CountStackDepthVisitor : public Thread::StackVisitor {
1040 CountStackDepthVisitor() : depth(0) {}
Elliott Hughes03181a82011-11-17 17:22:21 -08001041 virtual void VisitFrame(const Frame&, uintptr_t) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001042 ++depth;
1043 }
1044 size_t depth;
1045 };
1046 CountStackDepthVisitor visitor;
1047 DecodeThread(threadId)->WalkStack(&visitor);
1048 return visitor.depth;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001049}
1050
Elliott Hughes03181a82011-11-17 17:22:21 -08001051bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
1052 ScopedThreadListLock thread_list_lock;
1053 struct GetFrameVisitor : public Thread::StackVisitor {
1054 GetFrameVisitor(int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc)
1055 : found(false) ,depth(0), desired_frame_number(desired_frame_number), pFrameId(pFrameId), pLoc(pLoc) {
1056 }
1057 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
1058 if (!f.HasMethod()) {
1059 return; // These don't count?
1060 }
1061
1062 if (depth == desired_frame_number) {
1063 *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
1064
1065 Method* m = f.GetMethod();
1066 Class* c = m->GetDeclaringClass();
1067
1068 pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
1069 pLoc->classId = gRegistry->Add(c);
1070 pLoc->methodId = ToMethodId(m);
1071 pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
1072
1073 found = true;
1074 }
1075 ++depth;
1076 }
1077 bool found;
1078 int depth;
1079 int desired_frame_number;
1080 JDWP::FrameId* pFrameId;
1081 JDWP::JdwpLocation* pLoc;
1082 };
1083 GetFrameVisitor visitor(desired_frame_number, pFrameId, pLoc);
1084 visitor.desired_frame_number = desired_frame_number;
1085 DecodeThread(threadId)->WalkStack(&visitor);
1086 return visitor.found;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001087}
1088
1089JDWP::ObjectId Dbg::GetThreadSelfId() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001090 return gRegistry->Add(Thread::Current()->GetPeer());
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001091}
1092
Elliott Hughes475fc232011-10-25 15:00:35 -07001093void Dbg::SuspendVM() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001094 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 -07001095 Runtime::Current()->GetThreadList()->SuspendAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001096}
1097
1098void Dbg::ResumeVM() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001099 Runtime::Current()->GetThreadList()->ResumeAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001100}
1101
1102void Dbg::SuspendThread(JDWP::ObjectId threadId) {
1103 UNIMPLEMENTED(FATAL);
1104}
1105
1106void Dbg::ResumeThread(JDWP::ObjectId threadId) {
1107 UNIMPLEMENTED(FATAL);
1108}
1109
1110void Dbg::SuspendSelf() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001111 Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001112}
1113
1114bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
1115 UNIMPLEMENTED(FATAL);
1116 return false;
1117}
1118
Elliott Hughesdbb40792011-11-18 17:05:22 -08001119void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen) {
1120 Method** sp = reinterpret_cast<Method**>(frameId);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001121 Frame f;
1122 f.SetSP(sp);
1123 uint16_t reg = DemangleSlot(slot, f);
1124 Method* m = f.GetMethod();
1125
1126 const VmapTable vmap_table(m->GetVmapTableRaw());
1127 uint32_t vmap_offset;
1128 if (vmap_table.IsInContext(reg, vmap_offset)) {
1129 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1130 }
Elliott Hughesdbb40792011-11-18 17:05:22 -08001131
1132 switch (tag) {
1133 case JDWP::JT_BOOLEAN:
1134 {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001135 CHECK_EQ(expectedLen, 1U);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001136 uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
1137 LOG(WARNING) << "get boolean local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001138 JDWP::Set1(buf+1, intVal != 0);
1139 }
1140 break;
1141 case JDWP::JT_BYTE:
1142 {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001143 CHECK_EQ(expectedLen, 1U);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001144 uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
1145 LOG(WARNING) << "get byte local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001146 JDWP::Set1(buf+1, intVal);
1147 }
1148 break;
1149 case JDWP::JT_SHORT:
1150 case JDWP::JT_CHAR:
1151 {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001152 CHECK_EQ(expectedLen, 2U);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001153 uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
1154 LOG(WARNING) << "get short/char local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001155 JDWP::Set2BE(buf+1, intVal);
1156 }
1157 break;
1158 case JDWP::JT_INT:
1159 case JDWP::JT_FLOAT:
1160 {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001161 CHECK_EQ(expectedLen, 4U);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001162 uint32_t intVal = static_cast<uint32_t>(f.GetVReg(m, reg));
1163 LOG(WARNING) << "get int/float local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001164 JDWP::Set4BE(buf+1, intVal);
1165 }
1166 break;
1167 case JDWP::JT_ARRAY:
1168 {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001169 CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001170 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
1171 LOG(WARNING) << "get array local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001172 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001173 LOG(FATAL) << "reg " << reg << " expected to hold array: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001174 }
1175 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1176 }
1177 break;
1178 case JDWP::JT_OBJECT:
1179 {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001180 CHECK_EQ(expectedLen, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001181 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
1182 LOG(WARNING) << "get object local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001183 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001184 LOG(FATAL) << "reg " << reg << " expected to hold object: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001185 }
1186 tag = TagFromObject(o);
1187 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1188 }
1189 break;
1190 case JDWP::JT_DOUBLE:
1191 case JDWP::JT_LONG:
1192 {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001193 UNIMPLEMENTED(WARNING) << "get 64-bit local " << reg;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001194 CHECK_EQ(expectedLen, 8U);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001195 uint64_t longVal = 0; // memcpy(&longVal, &framePtr[reg], 8);
Elliott Hughesdbb40792011-11-18 17:05:22 -08001196 JDWP::Set8BE(buf+1, longVal);
1197 }
1198 break;
1199 default:
1200 LOG(FATAL) << "unknown tag " << tag;
1201 break;
1202 }
1203
1204 // Prepend tag, which may have been updated.
1205 JDWP::Set1(buf, tag);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001206}
1207
Elliott Hughesdbb40792011-11-18 17:05:22 -08001208void 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 -07001209 UNIMPLEMENTED(FATAL);
1210}
1211
1212void Dbg::PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags) {
1213 UNIMPLEMENTED(FATAL);
1214}
1215
1216void Dbg::PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception) {
1217 UNIMPLEMENTED(FATAL);
1218}
1219
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001220void Dbg::PostClassPrepare(Class* c) {
1221 UNIMPLEMENTED(FATAL);
1222}
1223
1224bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
1225 UNIMPLEMENTED(FATAL);
1226 return false;
1227}
1228
1229void Dbg::UnwatchLocation(const JDWP::JdwpLocation* pLoc) {
1230 UNIMPLEMENTED(FATAL);
1231}
1232
1233bool Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) {
1234 UNIMPLEMENTED(FATAL);
1235 return false;
1236}
1237
1238void Dbg::UnconfigureStep(JDWP::ObjectId threadId) {
1239 UNIMPLEMENTED(FATAL);
1240}
1241
1242JDWP::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) {
1243 UNIMPLEMENTED(FATAL);
1244 return JDWP::ERR_NONE;
1245}
1246
1247void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
1248 UNIMPLEMENTED(FATAL);
1249}
1250
1251void Dbg::RegisterObjectId(JDWP::ObjectId id) {
1252 UNIMPLEMENTED(FATAL);
1253}
1254
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001255/*
1256 * "buf" contains a full JDWP packet, possibly with multiple chunks. We
1257 * need to process each, accumulate the replies, and ship the whole thing
1258 * back.
1259 *
1260 * Returns "true" if we have a reply. The reply buffer is newly allocated,
1261 * and includes the chunk type/length, followed by the data.
1262 *
1263 * TODO: we currently assume that the request and reply include a single
1264 * chunk. If this becomes inconvenient we will need to adapt.
1265 */
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001266bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001267 CHECK_GE(dataLen, 0);
1268
1269 Thread* self = Thread::Current();
1270 JNIEnv* env = self->GetJniEnv();
1271
1272 static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
1273 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1274 static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
1275 "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
1276 static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
1277 static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
1278 static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
1279 static jfieldID type_fid = env->GetFieldID(Chunk_class, "type", "I");
1280
1281 // Create a byte[] corresponding to 'buf'.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001282 ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(dataLen));
1283 if (dataArray.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001284 LOG(WARNING) << "byte[] allocation failed: " << dataLen;
1285 env->ExceptionClear();
1286 return false;
1287 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001288 env->SetByteArrayRegion(dataArray.get(), 0, dataLen, reinterpret_cast<const jbyte*>(buf));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001289
1290 const int kChunkHdrLen = 8;
1291
1292 // Run through and find all chunks. [Currently just find the first.]
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001293 ScopedByteArrayRO contents(env, dataArray.get());
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001294 jint type = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[0]));
1295 jint length = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[4]));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001296 jint offset = kChunkHdrLen;
1297 if (offset + length > dataLen) {
1298 LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%d)", length, dataLen);
1299 return false;
1300 }
1301
1302 // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001303 ScopedLocalRef<jobject> chunk(env, env->CallStaticObjectMethod(DdmServer_class, dispatch_mid, type, dataArray.get(), offset, length));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001304 if (env->ExceptionCheck()) {
1305 LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
1306 env->ExceptionDescribe();
1307 env->ExceptionClear();
1308 return false;
1309 }
1310
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001311 if (chunk.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001312 return false;
1313 }
1314
1315 /*
1316 * Pull the pieces out of the chunk. We copy the results into a
1317 * newly-allocated buffer that the caller can free. We don't want to
1318 * continue using the Chunk object because nothing has a reference to it.
1319 *
1320 * We could avoid this by returning type/data/offset/length and having
1321 * the caller be aware of the object lifetime issues, but that
1322 * integrates the JDWP code more tightly into the VM, and doesn't work
1323 * if we have responses for multiple chunks.
1324 *
1325 * So we're pretty much stuck with copying data around multiple times.
1326 */
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001327 ScopedLocalRef<jbyteArray> replyData(env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk.get(), data_fid)));
1328 length = env->GetIntField(chunk.get(), length_fid);
1329 offset = env->GetIntField(chunk.get(), offset_fid);
1330 type = env->GetIntField(chunk.get(), type_fid);
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001331
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001332 LOG(VERBOSE) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
1333 if (length == 0 || replyData.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001334 return false;
1335 }
1336
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001337 jsize replyLength = env->GetArrayLength(replyData.get());
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001338 if (offset + length > replyLength) {
1339 LOG(WARNING) << StringPrintf("chunk off=%d len=%d exceeds reply array len %d", offset, length, replyLength);
1340 return false;
1341 }
1342
1343 uint8_t* reply = new uint8_t[length + kChunkHdrLen];
1344 if (reply == NULL) {
1345 LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
1346 return false;
1347 }
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001348 JDWP::Set4BE(reply + 0, type);
1349 JDWP::Set4BE(reply + 4, length);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001350 env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001351
1352 *pReplyBuf = reply;
1353 *pReplyLen = length + kChunkHdrLen;
1354
1355 LOG(VERBOSE) << StringPrintf("dvmHandleDdm returning type=%.4s buf=%p len=%d", (char*) reply, reply, length);
1356 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001357}
1358
Elliott Hughesa2155262011-11-16 16:26:58 -08001359void Dbg::DdmBroadcast(bool connect) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001360 LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
1361
1362 Thread* self = Thread::Current();
1363 if (self->GetState() != Thread::kRunnable) {
1364 LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
1365 /* try anyway? */
1366 }
1367
1368 JNIEnv* env = self->GetJniEnv();
1369 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1370 static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
1371 jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
1372 env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
1373 if (env->ExceptionCheck()) {
1374 LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
1375 env->ExceptionDescribe();
1376 env->ExceptionClear();
1377 }
1378}
1379
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001380void Dbg::DdmConnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001381 Dbg::DdmBroadcast(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001382}
1383
1384void Dbg::DdmDisconnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001385 Dbg::DdmBroadcast(false);
Elliott Hughes47fce012011-10-25 18:37:19 -07001386 gDdmThreadNotification = false;
1387}
1388
1389/*
Elliott Hughes82188472011-11-07 18:11:48 -08001390 * Send a notification when a thread starts, stops, or changes its name.
Elliott Hughes47fce012011-10-25 18:37:19 -07001391 *
1392 * Because we broadcast the full set of threads when the notifications are
1393 * first enabled, it's possible for "thread" to be actively executing.
1394 */
Elliott Hughes82188472011-11-07 18:11:48 -08001395void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001396 if (!gDdmThreadNotification) {
1397 return;
1398 }
1399
Elliott Hughes82188472011-11-07 18:11:48 -08001400 if (type == CHUNK_TYPE("THDE")) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001401 uint8_t buf[4];
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001402 JDWP::Set4BE(&buf[0], t->GetThinLockId());
Elliott Hughes47fce012011-10-25 18:37:19 -07001403 Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
Elliott Hughes82188472011-11-07 18:11:48 -08001404 } else {
1405 CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
1406 SirtRef<String> name(t->GetName());
1407 size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
1408 const jchar* chars = name->GetCharArray()->GetData();
1409
Elliott Hughes21f32d72011-11-09 17:44:13 -08001410 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001411 JDWP::Append4BE(bytes, t->GetThinLockId());
1412 JDWP::AppendUtf16BE(bytes, chars, char_count);
Elliott Hughes21f32d72011-11-09 17:44:13 -08001413 CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
1414 Dbg::DdmSendChunk(type, bytes);
Elliott Hughes47fce012011-10-25 18:37:19 -07001415 }
1416}
1417
Elliott Hughesa2155262011-11-16 16:26:58 -08001418static void DdmSendThreadStartCallback(Thread* t, void*) {
Elliott Hughes82188472011-11-07 18:11:48 -08001419 Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001420}
1421
1422void Dbg::DdmSetThreadNotification(bool enable) {
1423 // We lock the thread list to avoid sending duplicate events or missing
1424 // a thread change. We should be okay holding this lock while sending
1425 // the messages out. (We have to hold it while accessing a live thread.)
Elliott Hughesbbd9d832011-11-07 14:40:00 -08001426 ScopedThreadListLock thread_list_lock;
Elliott Hughes47fce012011-10-25 18:37:19 -07001427
1428 gDdmThreadNotification = enable;
1429 if (enable) {
Elliott Hughesbfe487b2011-10-26 15:48:55 -07001430 Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback, NULL);
Elliott Hughes47fce012011-10-25 18:37:19 -07001431 }
1432}
1433
Elliott Hughesa2155262011-11-16 16:26:58 -08001434void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001435 if (gDebuggerActive) {
1436 JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
Elliott Hughes82188472011-11-07 18:11:48 -08001437 gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001438 }
Elliott Hughes82188472011-11-07 18:11:48 -08001439 Dbg::DdmSendThreadNotification(t, type);
Elliott Hughes47fce012011-10-25 18:37:19 -07001440}
1441
1442void Dbg::PostThreadStart(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001443 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001444}
1445
1446void Dbg::PostThreadDeath(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001447 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001448}
1449
Elliott Hughes82188472011-11-07 18:11:48 -08001450void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001451 CHECK(buf != NULL);
1452 iovec vec[1];
1453 vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
1454 vec[0].iov_len = byte_count;
1455 Dbg::DdmSendChunkV(type, vec, 1);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001456}
1457
Elliott Hughes21f32d72011-11-09 17:44:13 -08001458void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
1459 DdmSendChunk(type, bytes.size(), &bytes[0]);
1460}
1461
Elliott Hughes82188472011-11-07 18:11:48 -08001462void Dbg::DdmSendChunkV(uint32_t type, const struct iovec* iov, int iovcnt) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001463 if (gJdwpState == NULL) {
1464 LOG(VERBOSE) << "Debugger thread not active, ignoring DDM send: " << type;
1465 } else {
Elliott Hughes376a7a02011-10-24 18:35:55 -07001466 gJdwpState->DdmSendChunkV(type, iov, iovcnt);
Elliott Hughes3bb81562011-10-21 18:52:59 -07001467 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001468}
1469
Elliott Hughes767a1472011-10-26 18:49:02 -07001470int Dbg::DdmHandleHpifChunk(HpifWhen when) {
1471 if (when == HPIF_WHEN_NOW) {
Elliott Hughes7162ad92011-10-27 14:08:42 -07001472 DdmSendHeapInfo(when);
Elliott Hughes767a1472011-10-26 18:49:02 -07001473 return true;
1474 }
1475
1476 if (when != HPIF_WHEN_NEVER && when != HPIF_WHEN_NEXT_GC && when != HPIF_WHEN_EVERY_GC) {
1477 LOG(ERROR) << "invalid HpifWhen value: " << static_cast<int>(when);
1478 return false;
1479 }
1480
1481 gDdmHpifWhen = when;
1482 return true;
1483}
1484
1485bool Dbg::DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when, Dbg::HpsgWhat what, bool native) {
1486 if (when != HPSG_WHEN_NEVER && when != HPSG_WHEN_EVERY_GC) {
1487 LOG(ERROR) << "invalid HpsgWhen value: " << static_cast<int>(when);
1488 return false;
1489 }
1490
1491 if (what != HPSG_WHAT_MERGED_OBJECTS && what != HPSG_WHAT_DISTINCT_OBJECTS) {
1492 LOG(ERROR) << "invalid HpsgWhat value: " << static_cast<int>(what);
1493 return false;
1494 }
1495
1496 if (native) {
1497 gDdmNhsgWhen = when;
1498 gDdmNhsgWhat = what;
1499 } else {
1500 gDdmHpsgWhen = when;
1501 gDdmHpsgWhat = what;
1502 }
1503 return true;
1504}
1505
Elliott Hughes7162ad92011-10-27 14:08:42 -07001506void Dbg::DdmSendHeapInfo(HpifWhen reason) {
1507 // If there's a one-shot 'when', reset it.
1508 if (reason == gDdmHpifWhen) {
1509 if (gDdmHpifWhen == HPIF_WHEN_NEXT_GC) {
1510 gDdmHpifWhen = HPIF_WHEN_NEVER;
1511 }
1512 }
1513
1514 /*
1515 * Chunk HPIF (client --> server)
1516 *
1517 * Heap Info. General information about the heap,
1518 * suitable for a summary display.
1519 *
1520 * [u4]: number of heaps
1521 *
1522 * For each heap:
1523 * [u4]: heap ID
1524 * [u8]: timestamp in ms since Unix epoch
1525 * [u1]: capture reason (same as 'when' value from server)
1526 * [u4]: max heap size in bytes (-Xmx)
1527 * [u4]: current heap size in bytes
1528 * [u4]: current number of bytes allocated
1529 * [u4]: current number of objects allocated
1530 */
1531 uint8_t heap_count = 1;
Elliott Hughes21f32d72011-11-09 17:44:13 -08001532 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001533 JDWP::Append4BE(bytes, heap_count);
1534 JDWP::Append4BE(bytes, 1); // Heap id (bogus; we only have one heap).
1535 JDWP::Append8BE(bytes, MilliTime());
1536 JDWP::Append1BE(bytes, reason);
1537 JDWP::Append4BE(bytes, Heap::GetMaxMemory()); // Max allowed heap size in bytes.
1538 JDWP::Append4BE(bytes, Heap::GetTotalMemory()); // Current heap size in bytes.
1539 JDWP::Append4BE(bytes, Heap::GetBytesAllocated());
1540 JDWP::Append4BE(bytes, Heap::GetObjectsAllocated());
Elliott Hughes21f32d72011-11-09 17:44:13 -08001541 CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
1542 Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
Elliott Hughes767a1472011-10-26 18:49:02 -07001543}
1544
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001545enum HpsgSolidity {
1546 SOLIDITY_FREE = 0,
1547 SOLIDITY_HARD = 1,
1548 SOLIDITY_SOFT = 2,
1549 SOLIDITY_WEAK = 3,
1550 SOLIDITY_PHANTOM = 4,
1551 SOLIDITY_FINALIZABLE = 5,
1552 SOLIDITY_SWEEP = 6,
1553};
1554
1555enum HpsgKind {
1556 KIND_OBJECT = 0,
1557 KIND_CLASS_OBJECT = 1,
1558 KIND_ARRAY_1 = 2,
1559 KIND_ARRAY_2 = 3,
1560 KIND_ARRAY_4 = 4,
1561 KIND_ARRAY_8 = 5,
1562 KIND_UNKNOWN = 6,
1563 KIND_NATIVE = 7,
1564};
1565
1566#define HPSG_PARTIAL (1<<7)
1567#define HPSG_STATE(solidity, kind) ((uint8_t)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
1568
1569struct HeapChunkContext {
1570 std::vector<uint8_t> buf;
1571 uint8_t* p;
1572 uint8_t* pieceLenField;
1573 size_t totalAllocationUnits;
Elliott Hughes82188472011-11-07 18:11:48 -08001574 uint32_t type;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001575 bool merge;
1576 bool needHeader;
1577
1578 // Maximum chunk size. Obtain this from the formula:
1579 // (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
1580 HeapChunkContext(bool merge, bool native)
1581 : buf(16384 - 16),
1582 type(0),
1583 merge(merge) {
1584 Reset();
1585 if (native) {
1586 type = CHUNK_TYPE("NHSG");
1587 } else {
1588 type = merge ? CHUNK_TYPE("HPSG") : CHUNK_TYPE("HPSO");
1589 }
1590 }
1591
1592 ~HeapChunkContext() {
1593 if (p > &buf[0]) {
1594 Flush();
1595 }
1596 }
1597
1598 void EnsureHeader(const void* chunk_ptr) {
1599 if (!needHeader) {
1600 return;
1601 }
1602
1603 // Start a new HPSx chunk.
1604 JDWP::Write4BE(&p, 1); // Heap id (bogus; we only have one heap).
1605 JDWP::Write1BE(&p, 8); // Size of allocation unit, in bytes.
1606
1607 JDWP::Write4BE(&p, reinterpret_cast<uintptr_t>(chunk_ptr)); // virtual address of segment start.
1608 JDWP::Write4BE(&p, 0); // offset of this piece (relative to the virtual address).
1609 // [u4]: length of piece, in allocation units
1610 // We won't know this until we're done, so save the offset and stuff in a dummy value.
1611 pieceLenField = p;
1612 JDWP::Write4BE(&p, 0x55555555);
1613 needHeader = false;
1614 }
1615
1616 void Flush() {
1617 // Patch the "length of piece" field.
1618 CHECK_LE(&buf[0], pieceLenField);
1619 CHECK_LE(pieceLenField, p);
1620 JDWP::Set4BE(pieceLenField, totalAllocationUnits);
1621
1622 Dbg::DdmSendChunk(type, p - &buf[0], &buf[0]);
1623 Reset();
1624 }
1625
Elliott Hughesa2155262011-11-16 16:26:58 -08001626 static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
1627 reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
1628 }
1629
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001630 private:
Elliott Hughesa2155262011-11-16 16:26:58 -08001631 enum { ALLOCATION_UNIT_SIZE = 8 };
1632
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001633 void Reset() {
1634 p = &buf[0];
1635 totalAllocationUnits = 0;
1636 needHeader = true;
1637 pieceLenField = NULL;
1638 }
1639
Elliott Hughesa2155262011-11-16 16:26:58 -08001640 void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
1641 CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001642
Elliott Hughesa2155262011-11-16 16:26:58 -08001643 /* Make sure there's enough room left in the buffer.
1644 * We need to use two bytes for every fractional 256
1645 * allocation units used by the chunk.
1646 */
1647 {
1648 size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
1649 size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1650 if (bytesLeft < needed) {
1651 Flush();
1652 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001653
Elliott Hughesa2155262011-11-16 16:26:58 -08001654 bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1655 if (bytesLeft < needed) {
1656 LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
1657 return;
1658 }
1659 }
1660
1661 // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
1662 EnsureHeader(chunk_ptr);
1663
1664 // Determine the type of this chunk.
1665 // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
1666 // If it's the same, we should combine them.
1667 uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
1668
1669 // Write out the chunk description.
1670 chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
1671 totalAllocationUnits += chunk_len;
1672 while (chunk_len > 256) {
1673 *p++ = state | HPSG_PARTIAL;
1674 *p++ = 255; // length - 1
1675 chunk_len -= 256;
1676 }
1677 *p++ = state;
1678 *p++ = chunk_len - 1;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001679 }
1680
Elliott Hughesa2155262011-11-16 16:26:58 -08001681 uint8_t ExamineObject(const Object* o, bool is_native_heap) {
1682 if (o == NULL) {
1683 return HPSG_STATE(SOLIDITY_FREE, 0);
1684 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001685
Elliott Hughesa2155262011-11-16 16:26:58 -08001686 // It's an allocated chunk. Figure out what it is.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001687
Elliott Hughesa2155262011-11-16 16:26:58 -08001688 // If we're looking at the native heap, we'll just return
1689 // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
1690 if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
1691 return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
1692 }
1693
1694 Class* c = o->GetClass();
1695 if (c == NULL) {
1696 // The object was probably just created but hasn't been initialized yet.
1697 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1698 }
1699
1700 if (!Heap::IsHeapAddress(c)) {
1701 LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
1702 return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
1703 }
1704
1705 if (c->IsClassClass()) {
1706 return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
1707 }
1708
1709 if (c->IsArrayClass()) {
1710 if (o->IsObjectArray()) {
1711 return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1712 }
1713 switch (c->GetComponentSize()) {
1714 case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
1715 case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
1716 case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1717 case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
1718 }
1719 }
1720
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001721 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1722 }
1723
Elliott Hughesa2155262011-11-16 16:26:58 -08001724 DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
1725};
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001726
1727void Dbg::DdmSendHeapSegments(bool native) {
1728 Dbg::HpsgWhen when;
1729 Dbg::HpsgWhat what;
1730 if (!native) {
1731 when = gDdmHpsgWhen;
1732 what = gDdmHpsgWhat;
1733 } else {
1734 when = gDdmNhsgWhen;
1735 what = gDdmNhsgWhat;
1736 }
1737 if (when == HPSG_WHEN_NEVER) {
1738 return;
1739 }
1740
1741 // Figure out what kind of chunks we'll be sending.
1742 CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
1743
1744 // First, send a heap start chunk.
1745 uint8_t heap_id[4];
1746 JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
1747 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
1748
1749 // Send a series of heap segment chunks.
Elliott Hughesa2155262011-11-16 16:26:58 -08001750 HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
1751 if (native) {
1752 dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
1753 } else {
1754 Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
1755 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001756
1757 // Finally, send a heap end chunk.
1758 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
Elliott Hughes767a1472011-10-26 18:49:02 -07001759}
1760
Elliott Hughes545a0642011-11-08 19:10:03 -08001761void Dbg::SetAllocTrackingEnabled(bool enabled) {
1762 MutexLock mu(gAllocTrackerLock);
1763 if (enabled) {
1764 if (recent_allocation_records_ == NULL) {
1765 LOG(INFO) << "Enabling alloc tracker (" << kNumAllocRecords << " entries, "
1766 << kMaxAllocRecordStackDepth << " frames --> "
1767 << (sizeof(AllocRecord) * kNumAllocRecords) << " bytes)";
1768 gAllocRecordHead = gAllocRecordCount = 0;
1769 recent_allocation_records_ = new AllocRecord[kNumAllocRecords];
1770 CHECK(recent_allocation_records_ != NULL);
1771 }
1772 } else {
1773 delete[] recent_allocation_records_;
1774 recent_allocation_records_ = NULL;
1775 }
1776}
1777
1778struct AllocRecordStackVisitor : public Thread::StackVisitor {
1779 AllocRecordStackVisitor(AllocRecord* record) : record(record), depth(0) {
1780 }
1781
1782 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
1783 if (depth >= kMaxAllocRecordStackDepth) {
1784 return;
1785 }
1786 Method* m = f.GetMethod();
1787 if (m == NULL || m->IsCalleeSaveMethod()) {
1788 return;
1789 }
1790 record->stack[depth].method = m;
1791 record->stack[depth].raw_pc = pc;
1792 ++depth;
1793 }
1794
1795 ~AllocRecordStackVisitor() {
1796 // Clear out any unused stack trace elements.
1797 for (; depth < kMaxAllocRecordStackDepth; ++depth) {
1798 record->stack[depth].method = NULL;
1799 record->stack[depth].raw_pc = 0;
1800 }
1801 }
1802
1803 AllocRecord* record;
1804 size_t depth;
1805};
1806
1807void Dbg::RecordAllocation(Class* type, size_t byte_count) {
1808 Thread* self = Thread::Current();
1809 CHECK(self != NULL);
1810
1811 MutexLock mu(gAllocTrackerLock);
1812 if (recent_allocation_records_ == NULL) {
1813 return;
1814 }
1815
1816 // Advance and clip.
1817 if (++gAllocRecordHead == kNumAllocRecords) {
1818 gAllocRecordHead = 0;
1819 }
1820
1821 // Fill in the basics.
1822 AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
1823 record->type = type;
1824 record->byte_count = byte_count;
1825 record->thin_lock_id = self->GetThinLockId();
1826
1827 // Fill in the stack trace.
1828 AllocRecordStackVisitor visitor(record);
1829 self->WalkStack(&visitor);
1830
1831 if (gAllocRecordCount < kNumAllocRecords) {
1832 ++gAllocRecordCount;
1833 }
1834}
1835
1836/*
1837 * Return the index of the head element.
1838 *
1839 * We point at the most-recently-written record, so if allocRecordCount is 1
1840 * we want to use the current element. Take "head+1" and subtract count
1841 * from it.
1842 *
1843 * We need to handle underflow in our circular buffer, so we add
1844 * kNumAllocRecords and then mask it back down.
1845 */
1846inline static int headIndex() {
1847 return (gAllocRecordHead+1 + kNumAllocRecords - gAllocRecordCount) & (kNumAllocRecords-1);
1848}
1849
1850void Dbg::DumpRecentAllocations() {
1851 MutexLock mu(gAllocTrackerLock);
1852 if (recent_allocation_records_ == NULL) {
1853 LOG(INFO) << "Not recording tracked allocations";
1854 return;
1855 }
1856
1857 // "i" is the head of the list. We want to start at the end of the
1858 // list and move forward to the tail.
1859 size_t i = headIndex();
1860 size_t count = gAllocRecordCount;
1861
1862 LOG(INFO) << "Tracked allocations, (head=" << gAllocRecordHead << " count=" << count << ")";
1863 while (count--) {
1864 AllocRecord* record = &recent_allocation_records_[i];
1865
1866 LOG(INFO) << StringPrintf(" T=%-2d %6d ", record->thin_lock_id, record->byte_count)
1867 << PrettyClass(record->type);
1868
1869 for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
1870 const Method* m = record->stack[stack_frame].method;
1871 if (m == NULL) {
1872 break;
1873 }
1874 LOG(INFO) << " " << PrettyMethod(m) << " line " << record->stack[stack_frame].LineNumber();
1875 }
1876
1877 // pause periodically to help logcat catch up
1878 if ((count % 5) == 0) {
1879 usleep(40000);
1880 }
1881
1882 i = (i + 1) & (kNumAllocRecords-1);
1883 }
1884}
1885
1886class StringTable {
1887 public:
1888 StringTable() {
1889 }
1890
1891 void Add(const String* s) {
1892 table_.insert(s);
1893 }
1894
1895 size_t IndexOf(const String* s) {
1896 return std::distance(table_.begin(), table_.find(s));
1897 }
1898
1899 size_t Size() {
1900 return table_.size();
1901 }
1902
1903 void WriteTo(std::vector<uint8_t>& bytes) {
1904 typedef std::set<const String*>::const_iterator It; // TODO: C++0x auto
1905 for (It it = table_.begin(); it != table_.end(); ++it) {
1906 const String* s = *it;
1907 JDWP::AppendUtf16BE(bytes, s->GetCharArray()->GetData(), s->GetLength());
1908 }
1909 }
1910
1911 private:
1912 std::set<const String*> table_;
1913 DISALLOW_COPY_AND_ASSIGN(StringTable);
1914};
1915
1916/*
1917 * The data we send to DDMS contains everything we have recorded.
1918 *
1919 * Message header (all values big-endian):
1920 * (1b) message header len (to allow future expansion); includes itself
1921 * (1b) entry header len
1922 * (1b) stack frame len
1923 * (2b) number of entries
1924 * (4b) offset to string table from start of message
1925 * (2b) number of class name strings
1926 * (2b) number of method name strings
1927 * (2b) number of source file name strings
1928 * For each entry:
1929 * (4b) total allocation size
1930 * (2b) threadId
1931 * (2b) allocated object's class name index
1932 * (1b) stack depth
1933 * For each stack frame:
1934 * (2b) method's class name
1935 * (2b) method name
1936 * (2b) method source file
1937 * (2b) line number, clipped to 32767; -2 if native; -1 if no source
1938 * (xb) class name strings
1939 * (xb) method name strings
1940 * (xb) source file strings
1941 *
1942 * As with other DDM traffic, strings are sent as a 4-byte length
1943 * followed by UTF-16 data.
1944 *
1945 * We send up 16-bit unsigned indexes into string tables. In theory there
1946 * can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
1947 * each table, but in practice there should be far fewer.
1948 *
1949 * The chief reason for using a string table here is to keep the size of
1950 * the DDMS message to a minimum. This is partly to make the protocol
1951 * efficient, but also because we have to form the whole thing up all at
1952 * once in a memory buffer.
1953 *
1954 * We use separate string tables for class names, method names, and source
1955 * files to keep the indexes small. There will generally be no overlap
1956 * between the contents of these tables.
1957 */
1958jbyteArray Dbg::GetRecentAllocations() {
1959 if (false) {
1960 DumpRecentAllocations();
1961 }
1962
1963 MutexLock mu(gAllocTrackerLock);
1964
1965 /*
1966 * Part 1: generate string tables.
1967 */
1968 StringTable class_names;
1969 StringTable method_names;
1970 StringTable filenames;
1971
1972 int count = gAllocRecordCount;
1973 int idx = headIndex();
1974 while (count--) {
1975 AllocRecord* record = &recent_allocation_records_[idx];
1976
1977 class_names.Add(record->type->GetDescriptor());
1978
1979 for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
1980 const Method* m = record->stack[i].method;
1981 if (m != NULL) {
1982 class_names.Add(m->GetDeclaringClass()->GetDescriptor());
1983 method_names.Add(m->GetName());
1984 filenames.Add(m->GetDeclaringClass()->GetSourceFile());
1985 }
1986 }
1987
1988 idx = (idx + 1) & (kNumAllocRecords-1);
1989 }
1990
1991 LOG(INFO) << "allocation records: " << gAllocRecordCount;
1992
1993 /*
1994 * Part 2: allocate a buffer and generate the output.
1995 */
1996 std::vector<uint8_t> bytes;
1997
1998 // (1b) message header len (to allow future expansion); includes itself
1999 // (1b) entry header len
2000 // (1b) stack frame len
2001 const int kMessageHeaderLen = 15;
2002 const int kEntryHeaderLen = 9;
2003 const int kStackFrameLen = 8;
2004 JDWP::Append1BE(bytes, kMessageHeaderLen);
2005 JDWP::Append1BE(bytes, kEntryHeaderLen);
2006 JDWP::Append1BE(bytes, kStackFrameLen);
2007
2008 // (2b) number of entries
2009 // (4b) offset to string table from start of message
2010 // (2b) number of class name strings
2011 // (2b) number of method name strings
2012 // (2b) number of source file name strings
2013 JDWP::Append2BE(bytes, gAllocRecordCount);
2014 size_t string_table_offset = bytes.size();
2015 JDWP::Append4BE(bytes, 0); // We'll patch this later...
2016 JDWP::Append2BE(bytes, class_names.Size());
2017 JDWP::Append2BE(bytes, method_names.Size());
2018 JDWP::Append2BE(bytes, filenames.Size());
2019
2020 count = gAllocRecordCount;
2021 idx = headIndex();
2022 while (count--) {
2023 // For each entry:
2024 // (4b) total allocation size
2025 // (2b) thread id
2026 // (2b) allocated object's class name index
2027 // (1b) stack depth
2028 AllocRecord* record = &recent_allocation_records_[idx];
2029 size_t stack_depth = record->GetDepth();
2030 JDWP::Append4BE(bytes, record->byte_count);
2031 JDWP::Append2BE(bytes, record->thin_lock_id);
2032 JDWP::Append2BE(bytes, class_names.IndexOf(record->type->GetDescriptor()));
2033 JDWP::Append1BE(bytes, stack_depth);
2034
2035 for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
2036 // For each stack frame:
2037 // (2b) method's class name
2038 // (2b) method name
2039 // (2b) method source file
2040 // (2b) line number, clipped to 32767; -2 if native; -1 if no source
2041 const Method* m = record->stack[stack_frame].method;
2042 JDWP::Append2BE(bytes, class_names.IndexOf(m->GetDeclaringClass()->GetDescriptor()));
2043 JDWP::Append2BE(bytes, method_names.IndexOf(m->GetName()));
2044 JDWP::Append2BE(bytes, filenames.IndexOf(m->GetDeclaringClass()->GetSourceFile()));
2045 JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
2046 }
2047
2048 idx = (idx + 1) & (kNumAllocRecords-1);
2049 }
2050
2051 // (xb) class name strings
2052 // (xb) method name strings
2053 // (xb) source file strings
2054 JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
2055 class_names.WriteTo(bytes);
2056 method_names.WriteTo(bytes);
2057 filenames.WriteTo(bytes);
2058
2059 JNIEnv* env = Thread::Current()->GetJniEnv();
2060 jbyteArray result = env->NewByteArray(bytes.size());
2061 if (result != NULL) {
2062 env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
2063 }
2064 return result;
2065}
2066
Elliott Hughes872d4ec2011-10-21 17:07:15 -07002067} // namespace art