blob: 3a6c154405676907a3ba6bb1eb23bdb18fde2282 [file] [log] [blame]
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "debugger.h"
18
Elliott Hughes3bb81562011-10-21 18:52:59 -070019#include <sys/uio.h>
20
Elliott Hughes545a0642011-11-08 19:10:03 -080021#include <set>
22
23#include "class_linker.h"
Elliott Hughes1bba14f2011-12-01 18:00:36 -080024#include "class_loader.h"
Elliott Hughes68fdbd02011-11-29 19:22:47 -080025#include "context.h"
Elliott Hughes6a5bd492011-10-28 14:33:57 -070026#include "ScopedLocalRef.h"
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -070027#include "ScopedPrimitiveArray.h"
Elliott Hughes47fce012011-10-25 18:37:19 -070028#include "stack_indirect_reference_table.h"
Elliott Hughes475fc232011-10-25 15:00:35 -070029#include "thread_list.h"
30
Elliott Hughes6a5bd492011-10-28 14:33:57 -070031extern "C" void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*);
32#ifndef HAVE_ANDROID_OS
33void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*), void*) {
34 // No-op for glibc.
35}
36#endif
37
Elliott Hughes872d4ec2011-10-21 17:07:15 -070038namespace art {
39
Elliott Hughes545a0642011-11-08 19:10:03 -080040static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
41static const size_t kNumAllocRecords = 512; // Must be power of 2.
42
Elliott Hughes475fc232011-10-25 15:00:35 -070043class ObjectRegistry {
44 public:
45 ObjectRegistry() : lock_("ObjectRegistry lock") {
46 }
47
48 JDWP::ObjectId Add(Object* o) {
49 if (o == NULL) {
50 return 0;
51 }
52 JDWP::ObjectId id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(o));
53 MutexLock mu(lock_);
54 map_[id] = o;
55 return id;
56 }
57
Elliott Hughes234ab152011-10-26 14:02:26 -070058 void Clear() {
59 MutexLock mu(lock_);
60 LOG(DEBUG) << "Debugger has detached; object registry had " << map_.size() << " entries";
61 map_.clear();
62 }
63
Elliott Hughes475fc232011-10-25 15:00:35 -070064 bool Contains(JDWP::ObjectId id) {
65 MutexLock mu(lock_);
66 return map_.find(id) != map_.end();
67 }
68
Elliott Hughesa2155262011-11-16 16:26:58 -080069 template<typename T> T Get(JDWP::ObjectId id) {
70 MutexLock mu(lock_);
71 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
72 It it = map_.find(id);
73 return (it != map_.end()) ? reinterpret_cast<T>(it->second) : NULL;
74 }
75
Elliott Hughesbfe487b2011-10-26 15:48:55 -070076 void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
77 MutexLock mu(lock_);
78 typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
79 for (It it = map_.begin(); it != map_.end(); ++it) {
80 visitor(it->second, arg);
81 }
82 }
83
Elliott Hughes475fc232011-10-25 15:00:35 -070084 private:
85 Mutex lock_;
86 std::map<JDWP::ObjectId, Object*> map_;
87};
88
Elliott Hughes545a0642011-11-08 19:10:03 -080089struct AllocRecordStackTraceElement {
90 const Method* method;
91 uintptr_t raw_pc;
92
93 int32_t LineNumber() const {
94 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
95 Class* c = method->GetDeclaringClass();
96 DexCache* dex_cache = c->GetDexCache();
97 const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
98 return dex_file.GetLineNumFromPC(method, method->ToDexPC(raw_pc));
99 }
100};
101
102struct AllocRecord {
103 Class* type;
104 size_t byte_count;
105 uint16_t thin_lock_id;
106 AllocRecordStackTraceElement stack[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
107
108 size_t GetDepth() {
109 size_t depth = 0;
110 while (depth < kMaxAllocRecordStackDepth && stack[depth].method != NULL) {
111 ++depth;
112 }
113 return depth;
114 }
115};
116
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700117// JDWP is allowed unless the Zygote forbids it.
118static bool gJdwpAllowed = true;
119
Elliott Hughes3bb81562011-10-21 18:52:59 -0700120// Was there a -Xrunjdwp or -agent argument on the command-line?
121static bool gJdwpConfigured = false;
122
123// Broken-down JDWP options. (Only valid if gJdwpConfigured is true.)
Elliott Hughes376a7a02011-10-24 18:35:55 -0700124static JDWP::JdwpOptions gJdwpOptions;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700125
126// Runtime JDWP state.
127static JDWP::JdwpState* gJdwpState = NULL;
128static bool gDebuggerConnected; // debugger or DDMS is connected.
129static bool gDebuggerActive; // debugger is making requests.
130
Elliott Hughes47fce012011-10-25 18:37:19 -0700131static bool gDdmThreadNotification = false;
132
Elliott Hughes767a1472011-10-26 18:49:02 -0700133// DDMS GC-related settings.
134static Dbg::HpifWhen gDdmHpifWhen = Dbg::HPIF_WHEN_NEVER;
135static Dbg::HpsgWhen gDdmHpsgWhen = Dbg::HPSG_WHEN_NEVER;
136static Dbg::HpsgWhat gDdmHpsgWhat;
137static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
138static Dbg::HpsgWhat gDdmNhsgWhat;
139
Elliott Hughes475fc232011-10-25 15:00:35 -0700140static ObjectRegistry* gRegistry = NULL;
141
Elliott Hughes545a0642011-11-08 19:10:03 -0800142// Recent allocation tracking.
143static Mutex gAllocTrackerLock("AllocTracker lock");
144AllocRecord* Dbg::recent_allocation_records_ = NULL; // TODO: CircularBuffer<AllocRecord>
145static size_t gAllocRecordHead = 0;
146static size_t gAllocRecordCount = 0;
147
Elliott Hughes24437992011-11-30 14:49:33 -0800148static JDWP::JdwpTag BasicTagFromDescriptor(const char* descriptor) {
149 // JDWP deliberately uses the descriptor characters' ASCII values for its enum.
150 // Note that by "basic" we mean that we don't get more specific than JT_OBJECT.
151 return static_cast<JDWP::JdwpTag>(descriptor[0]);
152}
153
154static JDWP::JdwpTag TagFromClass(Class* c) {
Elliott Hughes86b00102011-12-05 17:54:26 -0800155 CHECK(c != NULL);
Elliott Hughes24437992011-11-30 14:49:33 -0800156 if (c->IsArrayClass()) {
157 return JDWP::JT_ARRAY;
158 }
159
160 if (c->IsStringClass()) {
161 return JDWP::JT_STRING;
162 } else if (c->IsClassClass()) {
163 return JDWP::JT_CLASS_OBJECT;
164#if 0 // TODO
165 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
166 return JDWP::JT_THREAD;
167 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
168 return JDWP::JT_THREAD_GROUP;
169 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
170 return JDWP::JT_CLASS_LOADER;
171#endif
172 } else {
173 return JDWP::JT_OBJECT;
174 }
175}
176
177/*
178 * Objects declared to hold Object might actually hold a more specific
179 * type. The debugger may take a special interest in these (e.g. it
180 * wants to display the contents of Strings), so we want to return an
181 * appropriate tag.
182 *
183 * Null objects are tagged JT_OBJECT.
184 */
185static JDWP::JdwpTag TagFromObject(const Object* o) {
186 return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
187}
188
189static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
190 switch (tag) {
191 case JDWP::JT_BOOLEAN:
192 case JDWP::JT_BYTE:
193 case JDWP::JT_CHAR:
194 case JDWP::JT_FLOAT:
195 case JDWP::JT_DOUBLE:
196 case JDWP::JT_INT:
197 case JDWP::JT_LONG:
198 case JDWP::JT_SHORT:
199 case JDWP::JT_VOID:
200 return true;
201 default:
202 return false;
203 }
204}
205
Elliott Hughes3bb81562011-10-21 18:52:59 -0700206/*
207 * Handle one of the JDWP name/value pairs.
208 *
209 * JDWP options are:
210 * help: if specified, show help message and bail
211 * transport: may be dt_socket or dt_shmem
212 * address: for dt_socket, "host:port", or just "port" when listening
213 * server: if "y", wait for debugger to attach; if "n", attach to debugger
214 * timeout: how long to wait for debugger to connect / listen
215 *
216 * Useful with server=n (these aren't supported yet):
217 * onthrow=<exception-name>: connect to debugger when exception thrown
218 * onuncaught=y|n: connect to debugger when uncaught exception thrown
219 * launch=<command-line>: launch the debugger itself
220 *
221 * The "transport" option is required, as is "address" if server=n.
222 */
223static bool ParseJdwpOption(const std::string& name, const std::string& value) {
224 if (name == "transport") {
225 if (value == "dt_socket") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700226 gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700227 } else if (value == "dt_android_adb") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700228 gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700229 } else {
230 LOG(ERROR) << "JDWP transport not supported: " << value;
231 return false;
232 }
233 } else if (name == "server") {
234 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700235 gJdwpOptions.server = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700236 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700237 gJdwpOptions.server = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700238 } else {
239 LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
240 return false;
241 }
242 } else if (name == "suspend") {
243 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700244 gJdwpOptions.suspend = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700245 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700246 gJdwpOptions.suspend = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700247 } else {
248 LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
249 return false;
250 }
251 } else if (name == "address") {
252 /* this is either <port> or <host>:<port> */
253 std::string port_string;
Elliott Hughes376a7a02011-10-24 18:35:55 -0700254 gJdwpOptions.host.clear();
Elliott Hughes3bb81562011-10-21 18:52:59 -0700255 std::string::size_type colon = value.find(':');
256 if (colon != std::string::npos) {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700257 gJdwpOptions.host = value.substr(0, colon);
Elliott Hughes3bb81562011-10-21 18:52:59 -0700258 port_string = value.substr(colon + 1);
259 } else {
260 port_string = value;
261 }
262 if (port_string.empty()) {
263 LOG(ERROR) << "JDWP address missing port: " << value;
264 return false;
265 }
266 char* end;
267 long port = strtol(port_string.c_str(), &end, 10);
268 if (*end != '\0') {
269 LOG(ERROR) << "JDWP address has junk in port field: " << value;
270 return false;
271 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700272 gJdwpOptions.port = port;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700273 } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
274 /* valid but unsupported */
275 LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
276 } else {
277 LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
278 }
279
280 return true;
281}
282
283/*
284 * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
285 * "transport=dt_socket,address=8000,server=y,suspend=n"
286 */
287bool Dbg::ParseJdwpOptions(const std::string& options) {
Elliott Hughes47fce012011-10-25 18:37:19 -0700288 LOG(VERBOSE) << "ParseJdwpOptions: " << options;
289
Elliott Hughes3bb81562011-10-21 18:52:59 -0700290 std::vector<std::string> pairs;
291 Split(options, ',', pairs);
292
293 for (size_t i = 0; i < pairs.size(); ++i) {
294 std::string::size_type equals = pairs[i].find('=');
295 if (equals == std::string::npos) {
296 LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
297 return false;
298 }
299 ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
300 }
301
Elliott Hughes376a7a02011-10-24 18:35:55 -0700302 if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700303 LOG(ERROR) << "Must specify JDWP transport: " << options;
304 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700305 if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700306 LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
307 return false;
308 }
309
310 gJdwpConfigured = true;
311 return true;
312}
313
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700314void Dbg::StartJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700315 if (!gJdwpAllowed || !gJdwpConfigured) {
316 // No JDWP for you!
317 return;
318 }
319
Elliott Hughes475fc232011-10-25 15:00:35 -0700320 CHECK(gRegistry == NULL);
321 gRegistry = new ObjectRegistry;
322
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700323 // Init JDWP if the debugger is enabled. This may connect out to a
324 // debugger, passively listen for a debugger, or block waiting for a
325 // debugger.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700326 gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
327 if (gJdwpState == NULL) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -0800328 // We probably failed because some other process has the port already, which means that
329 // if we don't abort the user is likely to think they're talking to us when they're actually
330 // talking to that other process.
331 LOG(FATAL) << "debugger thread failed to initialize";
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700332 }
333
334 // If a debugger has already attached, send the "welcome" message.
335 // This may cause us to suspend all threads.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700336 if (gJdwpState->IsActive()) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800337 //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Elliott Hughes376a7a02011-10-24 18:35:55 -0700338 if (!gJdwpState->PostVMStart()) {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700339 LOG(WARNING) << "failed to post 'start' message to debugger";
340 }
341 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700342}
343
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700344void Dbg::StopJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700345 delete gJdwpState;
Elliott Hughes475fc232011-10-25 15:00:35 -0700346 delete gRegistry;
347 gRegistry = NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700348}
349
Elliott Hughes767a1472011-10-26 18:49:02 -0700350void Dbg::GcDidFinish() {
351 if (gDdmHpifWhen != HPIF_WHEN_NEVER) {
352 LOG(DEBUG) << "Sending VM heap info to DDM";
Elliott Hughes7162ad92011-10-27 14:08:42 -0700353 DdmSendHeapInfo(gDdmHpifWhen);
Elliott Hughes767a1472011-10-26 18:49:02 -0700354 }
355 if (gDdmHpsgWhen != HPSG_WHEN_NEVER) {
356 LOG(DEBUG) << "Dumping VM heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700357 DdmSendHeapSegments(false);
Elliott Hughes767a1472011-10-26 18:49:02 -0700358 }
359 if (gDdmNhsgWhen != HPSG_WHEN_NEVER) {
360 LOG(DEBUG) << "Dumping native heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700361 DdmSendHeapSegments(true);
Elliott Hughes767a1472011-10-26 18:49:02 -0700362 }
363}
364
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700365void Dbg::SetJdwpAllowed(bool allowed) {
366 gJdwpAllowed = allowed;
367}
368
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700369DebugInvokeReq* Dbg::GetInvokeReq() {
Elliott Hughes475fc232011-10-25 15:00:35 -0700370 return Thread::Current()->GetInvokeReq();
371}
372
373Thread* Dbg::GetDebugThread() {
374 return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
375}
376
377void Dbg::ClearWaitForEventThread() {
378 gJdwpState->ClearWaitForEventThread();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700379}
380
381void Dbg::Connected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700382 CHECK(!gDebuggerConnected);
383 LOG(VERBOSE) << "JDWP has attached";
384 gDebuggerConnected = true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700385}
386
Elliott Hughesa2155262011-11-16 16:26:58 -0800387void Dbg::GoActive() {
388 // Enable all debugging features, including scans for breakpoints.
389 // This is a no-op if we're already active.
390 // Only called from the JDWP handler thread.
391 if (gDebuggerActive) {
392 return;
393 }
394
395 LOG(INFO) << "Debugger is active";
396
397 // TODO: CHECK we don't have any outstanding breakpoints.
398
399 gDebuggerActive = true;
400
401 //dvmEnableAllSubMode(kSubModeDebuggerActive);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700402}
403
404void Dbg::Disconnected() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700405 CHECK(gDebuggerConnected);
406
407 gDebuggerActive = false;
408
409 //dvmDisableAllSubMode(kSubModeDebuggerActive);
410
411 gRegistry->Clear();
412 gDebuggerConnected = false;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700413}
414
415bool Dbg::IsDebuggerConnected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700416 return gDebuggerActive;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700417}
418
419bool Dbg::IsDebuggingEnabled() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700420 return gJdwpConfigured;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700421}
422
423int64_t Dbg::LastDebuggerActivity() {
Elliott Hughesca951522011-12-05 12:01:32 -0800424 return gJdwpState->LastDebuggerActivity();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700425}
426
427int Dbg::ThreadRunning() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700428 return static_cast<int>(Thread::Current()->SetState(Thread::kRunnable));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700429}
430
431int Dbg::ThreadWaiting() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700432 return static_cast<int>(Thread::Current()->SetState(Thread::kVmWait));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700433}
434
Elliott Hughes6ba581a2011-10-25 11:45:35 -0700435int Dbg::ThreadContinuing(int new_state) {
436 return static_cast<int>(Thread::Current()->SetState(static_cast<Thread::State>(new_state)));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700437}
438
439void Dbg::UndoDebuggerSuspensions() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700440 Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700441}
442
443void Dbg::Exit(int status) {
Elliott Hughes1bba14f2011-12-01 18:00:36 -0800444 exit(status); // This is all dalvik did.
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700445}
446
Elliott Hughesbfe487b2011-10-26 15:48:55 -0700447void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
448 if (gRegistry != NULL) {
449 gRegistry->VisitRoots(visitor, arg);
450 }
451}
452
Elliott Hughesa2155262011-11-16 16:26:58 -0800453std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
454 Class* c = gRegistry->Get<Class*>(classId);
455 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700456}
457
458JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
459 UNIMPLEMENTED(FATAL);
460 return 0;
461}
462
463JDWP::RefTypeId Dbg::GetSuperclass(JDWP::RefTypeId id) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800464 Class* c = gRegistry->Get<Class*>(id);
465 return gRegistry->Add(c->GetSuperClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700466}
467
468JDWP::ObjectId Dbg::GetClassLoader(JDWP::RefTypeId id) {
Elliott Hughes1bba14f2011-12-01 18:00:36 -0800469 Object* o = gRegistry->Get<Object*>(id);
470 return gRegistry->Add(o->GetClass()->GetClassLoader());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700471}
472
473uint32_t Dbg::GetAccessFlags(JDWP::RefTypeId id) {
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800474 Class* c = gRegistry->Get<Class*>(id);
475 return c->GetAccessFlags() & kAccJavaFlagsMask;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700476}
477
Elliott Hughesaed4be92011-12-02 16:16:23 -0800478bool Dbg::IsInterface(JDWP::RefTypeId classId) {
479 Class* c = gRegistry->Get<Class*>(classId);
480 return c->IsInterface();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700481}
482
Elliott Hughesa2155262011-11-16 16:26:58 -0800483void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
484 // Get the complete list of reference classes (i.e. all classes except
485 // the primitive types).
486 // Returns a newly-allocated buffer full of RefTypeId values.
487 struct ClassListCreator {
488 static bool Visit(Class* c, void* arg) {
489 return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
490 }
491
492 bool Visit(Class* c) {
493 if (!c->IsPrimitive()) {
494 classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
495 }
496 return true;
497 }
498
499 std::vector<JDWP::RefTypeId> classes;
500 };
501
502 ClassListCreator clc;
503 Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
504 *pClassCount = clc.classes.size();
505 *pClasses = new JDWP::RefTypeId[clc.classes.size()];
506 for (size_t i = 0; i < clc.classes.size(); ++i) {
507 (*pClasses)[i] = clc.classes[i];
508 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700509}
510
511void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
512 UNIMPLEMENTED(FATAL);
513}
514
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800515void Dbg::GetClassInfo(JDWP::RefTypeId classId, JDWP::JdwpTypeTag* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800516 Class* c = gRegistry->Get<Class*>(classId);
517 if (c->IsArrayClass()) {
518 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
519 *pTypeTag = JDWP::TT_ARRAY;
520 } else {
521 if (c->IsErroneous()) {
522 *pStatus = JDWP::CS_ERROR;
523 } else {
524 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
525 }
526 *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
527 }
528
529 if (pDescriptor != NULL) {
530 *pDescriptor = c->GetDescriptor()->ToModifiedUtf8();
531 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700532}
533
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800534void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids) {
535 std::vector<Class*> classes;
536 Runtime::Current()->GetClassLinker()->LookupClasses(descriptor, classes);
537 ids.clear();
538 for (size_t i = 0; i < classes.size(); ++i) {
539 ids.push_back(gRegistry->Add(classes[i]));
540 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700541}
542
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800543void Dbg::GetObjectType(JDWP::ObjectId objectId, JDWP::JdwpTypeTag* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800544 Object* o = gRegistry->Get<Object*>(objectId);
545 if (o->GetClass()->IsArrayClass()) {
546 *pRefTypeTag = JDWP::TT_ARRAY;
547 } else if (o->GetClass()->IsInterface()) {
548 *pRefTypeTag = JDWP::TT_INTERFACE;
549 } else {
550 *pRefTypeTag = JDWP::TT_CLASS;
551 }
552 *pRefTypeId = gRegistry->Add(o->GetClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700553}
554
555uint8_t Dbg::GetClassObjectType(JDWP::RefTypeId refTypeId) {
556 UNIMPLEMENTED(FATAL);
557 return 0;
558}
559
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800560std::string Dbg::GetSignature(JDWP::RefTypeId refTypeId) {
561 Class* c = gRegistry->Get<Class*>(refTypeId);
562 CHECK(c != NULL);
563 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700564}
565
Elliott Hughes03181a82011-11-17 17:22:21 -0800566bool Dbg::GetSourceFile(JDWP::RefTypeId refTypeId, std::string& result) {
567 Class* c = gRegistry->Get<Class*>(refTypeId);
568 CHECK(c != NULL);
569
570 String* source_file = c->GetSourceFile();
571 if (source_file == NULL) {
572 return false;
573 }
574 result = source_file->ToModifiedUtf8();
575 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700576}
577
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700578uint8_t Dbg::GetObjectTag(JDWP::ObjectId objectId) {
Elliott Hughes24437992011-11-30 14:49:33 -0800579 Object* o = gRegistry->Get<Object*>(objectId);
580 return TagFromObject(o);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700581}
582
Elliott Hughesaed4be92011-12-02 16:16:23 -0800583size_t Dbg::GetTagWidth(JDWP::JdwpTag tag) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800584 switch (tag) {
585 case JDWP::JT_VOID:
586 return 0;
587 case JDWP::JT_BYTE:
588 case JDWP::JT_BOOLEAN:
589 return 1;
590 case JDWP::JT_CHAR:
591 case JDWP::JT_SHORT:
592 return 2;
593 case JDWP::JT_FLOAT:
594 case JDWP::JT_INT:
595 return 4;
596 case JDWP::JT_ARRAY:
597 case JDWP::JT_OBJECT:
598 case JDWP::JT_STRING:
599 case JDWP::JT_THREAD:
600 case JDWP::JT_THREAD_GROUP:
601 case JDWP::JT_CLASS_LOADER:
602 case JDWP::JT_CLASS_OBJECT:
603 return sizeof(JDWP::ObjectId);
604 case JDWP::JT_DOUBLE:
605 case JDWP::JT_LONG:
606 return 8;
607 default:
608 LOG(FATAL) << "unknown tag " << tag;
609 return -1;
610 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700611}
612
613int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800614 Object* o = gRegistry->Get<Object*>(arrayId);
615 Array* a = o->AsArray();
616 return a->GetLength();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700617}
618
619uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
Elliott Hughes24437992011-11-30 14:49:33 -0800620 Object* o = gRegistry->Get<Object*>(arrayId);
621 Array* a = o->AsArray();
622 std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
623 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
624 if (!IsPrimitiveTag(tag)) {
625 tag = TagFromClass(a->GetClass()->GetComponentType());
626 }
627 return tag;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700628}
629
Elliott Hughes24437992011-11-30 14:49:33 -0800630bool Dbg::OutputArray(JDWP::ObjectId arrayId, int offset, int count, JDWP::ExpandBuf* pReply) {
631 Object* o = gRegistry->Get<Object*>(arrayId);
632 Array* a = o->AsArray();
633
634 if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
635 LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
636 return false;
637 }
638
639 std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
640 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
641
642 if (IsPrimitiveTag(tag)) {
643 size_t width = GetTagWidth(tag);
644 const uint8_t* src = reinterpret_cast<uint8_t*>(a->GetRawData());
645 uint8_t* dst = expandBufAddSpace(pReply, count * width);
646 if (width == 8) {
647 const uint64_t* src8 = reinterpret_cast<const uint64_t*>(src);
648 for (int i = 0; i < count; ++i) JDWP::Write8BE(&dst, src8[offset + i]);
649 } else if (width == 4) {
650 const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
651 for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[offset + i]);
652 } else if (width == 2) {
653 const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
654 for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[offset + i]);
655 } else {
656 memcpy(dst, &src[offset * width], count * width);
657 }
658 } else {
659 ObjectArray<Object>* oa = a->AsObjectArray<Object>();
660 for (int i = 0; i < count; ++i) {
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800661 Object* element = oa->Get(offset + i);
Elliott Hughes24437992011-11-30 14:49:33 -0800662 JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
663 expandBufAdd1(pReply, specific_tag);
664 expandBufAddObjectId(pReply, gRegistry->Add(element));
665 }
666 }
667
668 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700669}
670
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800671bool Dbg::SetArrayElements(JDWP::ObjectId arrayId, int offset, int count, const uint8_t* src) {
672 Object* o = gRegistry->Get<Object*>(arrayId);
673 Array* a = o->AsArray();
674
675 if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
676 LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
677 return false;
678 }
679
680 std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
681 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
682
683 if (IsPrimitiveTag(tag)) {
684 size_t width = GetTagWidth(tag);
685 uint8_t* dst = &(reinterpret_cast<uint8_t*>(a->GetRawData())[offset * width]);
686 if (width == 8) {
687 for (int i = 0; i < count; ++i) {
688 // Handle potentially non-aligned memory access one byte at a time for ARM's benefit.
689 uint64_t value;
690 for (size_t j = 0; j < sizeof(uint64_t); ++j) reinterpret_cast<uint8_t*>(&value)[j] = src[j];
691 src += sizeof(uint64_t);
692 JDWP::Write8BE(&dst, value);
693 }
694 } else if (width == 4) {
695 const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
696 for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[i]);
697 } else if (width == 2) {
698 const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
699 for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[i]);
700 } else {
701 memcpy(&dst[offset * width], src, count * width);
702 }
703 } else {
704 ObjectArray<Object>* oa = a->AsObjectArray<Object>();
705 for (int i = 0; i < count; ++i) {
706 JDWP::ObjectId id = JDWP::ReadObjectId(&src);
707 oa->Set(offset + i, gRegistry->Get<Object*>(id));
708 }
709 }
710
711 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700712}
713
714JDWP::ObjectId Dbg::CreateString(const char* str) {
Elliott Hughescccd84f2011-12-05 16:51:54 -0800715 return gRegistry->Add(String::AllocFromModifiedUtf8(str));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700716}
717
718JDWP::ObjectId Dbg::CreateObject(JDWP::RefTypeId classId) {
Elliott Hughescccd84f2011-12-05 16:51:54 -0800719 Class* c = gRegistry->Get<Class*>(classId);
720 return gRegistry->Add(c->AllocObject());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700721}
722
723JDWP::ObjectId Dbg::CreateArrayObject(JDWP::RefTypeId arrayTypeId, uint32_t length) {
724 UNIMPLEMENTED(FATAL);
725 return 0;
726}
727
728bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
729 UNIMPLEMENTED(FATAL);
730 return false;
731}
732
Elliott Hughes03181a82011-11-17 17:22:21 -0800733JDWP::FieldId ToFieldId(Field* f) {
734#ifdef MOVING_GARBAGE_COLLECTOR
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700735 UNIMPLEMENTED(FATAL);
Elliott Hughes03181a82011-11-17 17:22:21 -0800736#else
737 return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
738#endif
739}
740
741JDWP::MethodId ToMethodId(Method* m) {
742#ifdef MOVING_GARBAGE_COLLECTOR
743 UNIMPLEMENTED(FATAL);
744#else
745 return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
746#endif
747}
748
Elliott Hughesaed4be92011-12-02 16:16:23 -0800749Field* FromFieldId(JDWP::FieldId fid) {
750#ifdef MOVING_GARBAGE_COLLECTOR
751 UNIMPLEMENTED(FATAL);
752#else
753 return reinterpret_cast<Field*>(static_cast<uintptr_t>(fid));
754#endif
755}
756
Elliott Hughes03181a82011-11-17 17:22:21 -0800757Method* FromMethodId(JDWP::MethodId mid) {
758#ifdef MOVING_GARBAGE_COLLECTOR
759 UNIMPLEMENTED(FATAL);
760#else
761 return reinterpret_cast<Method*>(static_cast<uintptr_t>(mid));
762#endif
763}
764
765std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
766 return FromMethodId(methodId)->GetName()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700767}
768
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800769/*
770 * Augment the access flags for synthetic methods and fields by setting
771 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
772 * flags not specified by the Java programming language.
773 */
774static uint32_t MangleAccessFlags(uint32_t accessFlags) {
775 accessFlags &= kAccJavaFlagsMask;
776 if ((accessFlags & kAccSynthetic) != 0) {
777 accessFlags |= 0xf0000000;
778 }
779 return accessFlags;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700780}
781
Elliott Hughesdbb40792011-11-18 17:05:22 -0800782static const uint16_t kEclipseWorkaroundSlot = 1000;
783
784/*
785 * Eclipse appears to expect that the "this" reference is in slot zero.
786 * If it's not, the "variables" display will show two copies of "this",
787 * possibly because it gets "this" from SF.ThisObject and then displays
788 * all locals with nonzero slot numbers.
789 *
790 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
791 * SF.GetValues / SF.SetValues we map them back.
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800792 *
793 * TODO: jdb uses the value to determine whether a variable is a local or an argument,
794 * by checking whether it's less than the number of arguments. To make that work, we'd
795 * have to "mangle" all the arguments to come first, not just the implicit argument 'this'.
Elliott Hughesdbb40792011-11-18 17:05:22 -0800796 */
797static uint16_t MangleSlot(uint16_t slot, const char* name) {
798 uint16_t newSlot = slot;
799 if (strcmp(name, "this") == 0) {
800 newSlot = 0;
801 } else if (slot == 0) {
802 newSlot = kEclipseWorkaroundSlot;
803 }
804 return newSlot;
805}
806
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800807static uint16_t DemangleSlot(uint16_t slot, Frame& f) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800808 if (slot == kEclipseWorkaroundSlot) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800809 return 0;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800810 } else if (slot == 0) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800811 Method* m = f.GetMethod();
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800812 return m->NumRegisters() - m->NumIns();
Elliott Hughesdbb40792011-11-18 17:05:22 -0800813 }
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800814 return slot;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800815}
816
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800817void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800818 Class* c = gRegistry->Get<Class*>(refTypeId);
819 CHECK(c != NULL);
820
821 size_t instance_field_count = c->NumInstanceFields();
822 size_t static_field_count = c->NumStaticFields();
823
824 expandBufAdd4BE(pReply, instance_field_count + static_field_count);
825
826 for (size_t i = 0; i < instance_field_count + static_field_count; ++i) {
827 Field* f = (i < instance_field_count) ? c->GetInstanceField(i) : c->GetStaticField(i - instance_field_count);
828
829 expandBufAddFieldId(pReply, ToFieldId(f));
830 expandBufAddUtf8String(pReply, f->GetName()->ToModifiedUtf8().c_str());
831 expandBufAddUtf8String(pReply, f->GetTypeDescriptor());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800832 if (with_generic) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800833 static const char genericSignature[1] = "";
834 expandBufAddUtf8String(pReply, genericSignature);
835 }
836 expandBufAdd4BE(pReply, MangleAccessFlags(f->GetAccessFlags()));
837 }
838}
839
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800840void Dbg::OutputDeclaredMethods(JDWP::RefTypeId refTypeId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800841 Class* c = gRegistry->Get<Class*>(refTypeId);
842 CHECK(c != NULL);
843
844 size_t direct_method_count = c->NumDirectMethods();
845 size_t virtual_method_count = c->NumVirtualMethods();
846
847 expandBufAdd4BE(pReply, direct_method_count + virtual_method_count);
848
849 for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) {
850 Method* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count);
851
852 expandBufAddMethodId(pReply, ToMethodId(m));
853 expandBufAddUtf8String(pReply, m->GetName()->ToModifiedUtf8().c_str());
854 expandBufAddUtf8String(pReply, m->GetSignature()->ToModifiedUtf8().c_str());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800855 if (with_generic) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800856 static const char genericSignature[1] = "";
857 expandBufAddUtf8String(pReply, genericSignature);
858 }
859 expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags()));
860 }
861}
862
863void Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId refTypeId, JDWP::ExpandBuf* pReply) {
864 Class* c = gRegistry->Get<Class*>(refTypeId);
865 CHECK(c != NULL);
866 size_t interface_count = c->NumInterfaces();
867 expandBufAdd4BE(pReply, interface_count);
868 for (size_t i = 0; i < interface_count; ++i) {
869 expandBufAddRefTypeId(pReply, gRegistry->Add(c->GetInterface(i)));
870 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700871}
872
873void Dbg::OutputLineTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, JDWP::ExpandBuf* pReply) {
Elliott Hughes03181a82011-11-17 17:22:21 -0800874 struct DebugCallbackContext {
875 int numItems;
876 JDWP::ExpandBuf* pReply;
877
878 static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
879 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
880 expandBufAdd8BE(pContext->pReply, address);
881 expandBufAdd4BE(pContext->pReply, lineNum);
882 pContext->numItems++;
883 return true;
884 }
885 };
886
887 Method* m = FromMethodId(methodId);
888 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
889 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
890 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
891
892 uint64_t start, end;
893 if (m->IsNative()) {
894 start = -1;
895 end = -1;
896 } else {
897 start = 0;
898 end = code_item->insns_size_in_code_units_; // TODO: what are the units supposed to be? *2?
899 }
900
901 expandBufAdd8BE(pReply, start);
902 expandBufAdd8BE(pReply, end);
903
904 // Add numLines later
905 size_t numLinesOffset = expandBufGetLength(pReply);
906 expandBufAdd4BE(pReply, 0);
907
908 DebugCallbackContext context;
909 context.numItems = 0;
910 context.pReply = pReply;
911
912 dex_file.DecodeDebugInfo(code_item, m, DebugCallbackContext::Callback, NULL, &context);
913
914 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700915}
916
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800917void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800918 struct DebugCallbackContext {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800919 JDWP::ExpandBuf* pReply;
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800920 size_t variable_count;
921 bool with_generic;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800922
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800923 static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char* name, const char* descriptor, const char* signature) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800924 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
925
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800926 LOG(VERBOSE) << StringPrintf(" %2d: %d(%d) '%s' '%s' '%s' slot=%d", pContext->variable_count, startAddress, endAddress - startAddress, name, descriptor, signature, slot);
Elliott Hughesdbb40792011-11-18 17:05:22 -0800927
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800928 slot = MangleSlot(slot, name);
929
Elliott Hughesdbb40792011-11-18 17:05:22 -0800930 expandBufAdd8BE(pContext->pReply, startAddress);
931 expandBufAddUtf8String(pContext->pReply, name);
932 expandBufAddUtf8String(pContext->pReply, descriptor);
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800933 if (pContext->with_generic) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800934 expandBufAddUtf8String(pContext->pReply, signature);
935 }
936 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
937 expandBufAdd4BE(pContext->pReply, slot);
938
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800939 ++pContext->variable_count;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800940 }
941 };
942
943 Method* m = FromMethodId(methodId);
944 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
945 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
946 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
947
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800948 // arg_count considers doubles and longs to take 2 units.
949 // variable_count considers everything to take 1 unit.
950 std::string shorty(m->GetShorty()->ToModifiedUtf8());
951 expandBufAdd4BE(pReply, m->NumArgRegisters(shorty));
Elliott Hughesdbb40792011-11-18 17:05:22 -0800952
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800953 // We don't know the total number of variables yet, so leave a blank and update it later.
954 size_t variable_count_offset = expandBufGetLength(pReply);
Elliott Hughesdbb40792011-11-18 17:05:22 -0800955 expandBufAdd4BE(pReply, 0);
956
957 DebugCallbackContext context;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800958 context.pReply = pReply;
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800959 context.variable_count = 0;
960 context.with_generic = with_generic;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800961
962 dex_file.DecodeDebugInfo(code_item, m, NULL, DebugCallbackContext::Callback, &context);
963
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800964 JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700965}
966
Elliott Hughesaed4be92011-12-02 16:16:23 -0800967JDWP::JdwpTag Dbg::GetFieldBasicTag(JDWP::FieldId fieldId) {
968 return BasicTagFromDescriptor(FromFieldId(fieldId)->GetTypeDescriptor());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700969}
970
Elliott Hughesaed4be92011-12-02 16:16:23 -0800971JDWP::JdwpTag Dbg::GetStaticFieldBasicTag(JDWP::FieldId fieldId) {
972 return BasicTagFromDescriptor(FromFieldId(fieldId)->GetTypeDescriptor());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700973}
974
975void Dbg::GetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
Elliott Hughesaed4be92011-12-02 16:16:23 -0800976 Object* o = gRegistry->Get<Object*>(objectId);
977 Field* f = FromFieldId(fieldId);
978
979 JDWP::JdwpTag tag = BasicTagFromDescriptor(f->GetTypeDescriptor());
980
981 if (IsPrimitiveTag(tag)) {
982 expandBufAdd1(pReply, tag);
983 if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) {
984 expandBufAdd1(pReply, f->Get32(o));
985 } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) {
986 expandBufAdd2BE(pReply, f->Get32(o));
987 } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) {
988 expandBufAdd4BE(pReply, f->Get32(o));
989 } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
990 expandBufAdd8BE(pReply, f->Get64(o));
991 } else {
992 LOG(FATAL) << "unknown tag: " << tag;
993 }
994 } else {
995 Object* value = f->GetObject(o);
996 expandBufAdd1(pReply, TagFromObject(value));
997 expandBufAddObjectId(pReply, gRegistry->Add(value));
998 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700999}
1000
1001void Dbg::SetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, uint64_t value, int width) {
Elliott Hughesaed4be92011-12-02 16:16:23 -08001002 Object* o = gRegistry->Get<Object*>(objectId);
1003 Field* f = FromFieldId(fieldId);
1004
1005 JDWP::JdwpTag tag = BasicTagFromDescriptor(f->GetTypeDescriptor());
1006
1007 if (IsPrimitiveTag(tag)) {
1008 if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
1009 f->Set64(o, value);
1010 } else {
1011 f->Set32(o, value);
1012 }
1013 } else {
1014 f->SetObject(o, gRegistry->Get<Object*>(value));
1015 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001016}
1017
Elliott Hughes6fa602d2011-12-02 17:54:25 -08001018void Dbg::GetStaticFieldValue(JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
1019 GetFieldValue(0, fieldId, pReply);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001020}
1021
Elliott Hughes6fa602d2011-12-02 17:54:25 -08001022void Dbg::SetStaticFieldValue(JDWP::FieldId fieldId, uint64_t value, int width) {
1023 SetFieldValue(0, fieldId, value, width);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001024}
1025
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001026std::string Dbg::StringToUtf8(JDWP::ObjectId strId) {
1027 String* s = gRegistry->Get<String*>(strId);
1028 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001029}
1030
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001031Thread* DecodeThread(JDWP::ObjectId threadId) {
1032 Object* thread_peer = gRegistry->Get<Object*>(threadId);
1033 CHECK(thread_peer != NULL);
1034 return Thread::FromManagedThread(thread_peer);
1035}
1036
1037bool Dbg::GetThreadName(JDWP::ObjectId threadId, std::string& name) {
1038 ScopedThreadListLock thread_list_lock;
1039 Thread* thread = DecodeThread(threadId);
1040 if (thread == NULL) {
1041 return false;
1042 }
1043 StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetName()->ToModifiedUtf8().c_str());
1044 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001045}
1046
1047JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
Elliott Hughes499c5132011-11-17 14:55:11 -08001048 Object* thread = gRegistry->Get<Object*>(threadId);
1049 CHECK(thread != NULL);
1050
1051 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
1052 CHECK(c != NULL);
1053 Field* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
1054 CHECK(f != NULL);
1055 Object* group = f->GetObject(thread);
1056 CHECK(group != NULL);
1057 return gRegistry->Add(group);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001058}
1059
Elliott Hughes499c5132011-11-17 14:55:11 -08001060std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
1061 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
1062 CHECK(thread_group != NULL);
1063
1064 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1065 CHECK(c != NULL);
1066 Field* f = c->FindInstanceField("name", "Ljava/lang/String;");
1067 CHECK(f != NULL);
1068 String* s = reinterpret_cast<String*>(f->GetObject(thread_group));
1069 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001070}
1071
1072JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001073 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
1074 CHECK(thread_group != NULL);
1075
1076 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1077 CHECK(c != NULL);
1078 Field* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
1079 CHECK(f != NULL);
1080 Object* parent = f->GetObject(thread_group);
1081 return gRegistry->Add(parent);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001082}
1083
Elliott Hughes499c5132011-11-17 14:55:11 -08001084static Object* GetStaticThreadGroup(const char* field_name) {
1085 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1086 CHECK(c != NULL);
1087 Field* f = c->FindStaticField(field_name, "Ljava/lang/ThreadGroup;");
1088 CHECK(f != NULL);
1089 Object* group = f->GetObject(NULL);
1090 CHECK(group != NULL);
1091 return group;
1092}
1093
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001094JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -08001095 return gRegistry->Add(GetStaticThreadGroup("mSystem"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001096}
1097
1098JDWP::ObjectId Dbg::GetMainThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -08001099 return gRegistry->Add(GetStaticThreadGroup("mMain"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001100}
1101
Elliott Hughes499c5132011-11-17 14:55:11 -08001102bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* pThreadStatus, uint32_t* pSuspendStatus) {
1103 ScopedThreadListLock thread_list_lock;
1104
1105 Thread* thread = DecodeThread(threadId);
1106 if (thread == NULL) {
1107 return false;
1108 }
1109
1110 switch (thread->GetState()) {
1111 case Thread::kTerminated: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1112 case Thread::kRunnable: *pThreadStatus = JDWP::TS_RUNNING; break;
1113 case Thread::kTimedWaiting: *pThreadStatus = JDWP::TS_SLEEPING; break;
1114 case Thread::kBlocked: *pThreadStatus = JDWP::TS_MONITOR; break;
1115 case Thread::kWaiting: *pThreadStatus = JDWP::TS_WAIT; break;
1116 case Thread::kInitializing: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1117 case Thread::kStarting: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1118 case Thread::kNative: *pThreadStatus = JDWP::TS_RUNNING; break;
1119 case Thread::kVmWait: *pThreadStatus = JDWP::TS_WAIT; break;
1120 case Thread::kSuspended: *pThreadStatus = JDWP::TS_RUNNING; break;
1121 default:
1122 LOG(FATAL) << "unknown thread state " << thread->GetState();
1123 }
1124
1125 *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : 0);
1126
1127 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001128}
1129
1130uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
1131 UNIMPLEMENTED(FATAL);
1132 return 0;
1133}
1134
1135bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -08001136 return DecodeThread(threadId) != NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001137}
1138
1139bool Dbg::IsSuspended(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -08001140 return DecodeThread(threadId)->IsSuspended();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001141}
1142
1143//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
1144
Elliott Hughesa2155262011-11-16 16:26:58 -08001145void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
1146 struct ThreadListVisitor {
1147 static void Visit(Thread* t, void* arg) {
1148 reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
1149 }
1150
1151 void Visit(Thread* t) {
1152 if (t == Dbg::GetDebugThread()) {
1153 // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
1154 // query all threads, so it's easier if we just don't tell them about this thread.
1155 return;
1156 }
1157 if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
1158 threads.push_back(gRegistry->Add(t->GetPeer()));
1159 }
1160 }
1161
1162 Object* thread_group;
1163 std::vector<JDWP::ObjectId> threads;
1164 };
1165
1166 ThreadListVisitor tlv;
1167 tlv.thread_group = thread_group;
1168
1169 {
1170 ScopedThreadListLock thread_list_lock;
1171 Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
1172 }
1173
1174 *pThreadCount = tlv.threads.size();
1175 if (*pThreadCount == 0) {
1176 *ppThreadIds = NULL;
1177 } else {
1178 *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
1179 for (size_t i = 0; i < *pThreadCount; ++i) {
1180 (*ppThreadIds)[i] = tlv.threads[i];
1181 }
1182 }
1183}
1184
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001185void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001186 GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001187}
1188
1189void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001190 GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001191}
1192
1193int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
Elliott Hughes03181a82011-11-17 17:22:21 -08001194 ScopedThreadListLock thread_list_lock;
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001195 struct CountStackDepthVisitor : public Thread::StackVisitor {
1196 CountStackDepthVisitor() : depth(0) {}
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001197 virtual void VisitFrame(const Frame& f, uintptr_t) {
1198 // TODO: we'll need to skip callee-save frames too.
1199 if (f.HasMethod()) {
1200 ++depth;
1201 }
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001202 }
1203 size_t depth;
1204 };
1205 CountStackDepthVisitor visitor;
1206 DecodeThread(threadId)->WalkStack(&visitor);
1207 return visitor.depth;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001208}
1209
Elliott Hughes03181a82011-11-17 17:22:21 -08001210bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
1211 ScopedThreadListLock thread_list_lock;
1212 struct GetFrameVisitor : public Thread::StackVisitor {
1213 GetFrameVisitor(int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc)
1214 : found(false) ,depth(0), desired_frame_number(desired_frame_number), pFrameId(pFrameId), pLoc(pLoc) {
1215 }
1216 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001217 // TODO: we'll need to skip callee-save frames too.
Elliott Hughes03181a82011-11-17 17:22:21 -08001218 if (!f.HasMethod()) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001219 return; // The debugger can't do anything useful with a frame that has no Method*.
Elliott Hughes03181a82011-11-17 17:22:21 -08001220 }
1221
1222 if (depth == desired_frame_number) {
1223 *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
1224
1225 Method* m = f.GetMethod();
1226 Class* c = m->GetDeclaringClass();
1227
1228 pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
1229 pLoc->classId = gRegistry->Add(c);
1230 pLoc->methodId = ToMethodId(m);
1231 pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
1232
1233 found = true;
1234 }
1235 ++depth;
1236 }
1237 bool found;
1238 int depth;
1239 int desired_frame_number;
1240 JDWP::FrameId* pFrameId;
1241 JDWP::JdwpLocation* pLoc;
1242 };
1243 GetFrameVisitor visitor(desired_frame_number, pFrameId, pLoc);
1244 visitor.desired_frame_number = desired_frame_number;
1245 DecodeThread(threadId)->WalkStack(&visitor);
1246 return visitor.found;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001247}
1248
1249JDWP::ObjectId Dbg::GetThreadSelfId() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001250 return gRegistry->Add(Thread::Current()->GetPeer());
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001251}
1252
Elliott Hughes475fc232011-10-25 15:00:35 -07001253void Dbg::SuspendVM() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001254 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 -07001255 Runtime::Current()->GetThreadList()->SuspendAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001256}
1257
1258void Dbg::ResumeVM() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001259 Runtime::Current()->GetThreadList()->ResumeAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001260}
1261
1262void Dbg::SuspendThread(JDWP::ObjectId threadId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001263 Object* peer = gRegistry->Get<Object*>(threadId);
1264 ScopedThreadListLock thread_list_lock;
1265 Thread* thread = Thread::FromManagedThread(peer);
1266 if (thread == NULL) {
1267 LOG(WARNING) << "No such thread for suspend: " << peer;
1268 return;
1269 }
1270 Runtime::Current()->GetThreadList()->Suspend(thread, true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001271}
1272
1273void Dbg::ResumeThread(JDWP::ObjectId threadId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001274 Object* peer = gRegistry->Get<Object*>(threadId);
1275 ScopedThreadListLock thread_list_lock;
1276 Thread* thread = Thread::FromManagedThread(peer);
1277 if (thread == NULL) {
1278 LOG(WARNING) << "No such thread for resume: " << peer;
1279 return;
1280 }
1281 Runtime::Current()->GetThreadList()->Resume(thread, true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001282}
1283
1284void Dbg::SuspendSelf() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001285 Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001286}
1287
1288bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
Elliott Hughes86b00102011-12-05 17:54:26 -08001289 Method** sp = reinterpret_cast<Method**>(frameId);
1290 Frame f;
1291 f.SetSP(sp);
1292 uint16_t reg = DemangleSlot(0, f);
1293 Method* m = f.GetMethod();
1294
1295 Object* o = NULL;
1296 if (!m->IsNative() && !m->IsStatic()) {
1297 o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
1298 }
1299 *pThisId = gRegistry->Add(o);
1300 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001301}
1302
Elliott Hughescccd84f2011-12-05 16:51:54 -08001303void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
Elliott Hughesdbb40792011-11-18 17:05:22 -08001304 Method** sp = reinterpret_cast<Method**>(frameId);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001305 Frame f;
1306 f.SetSP(sp);
1307 uint16_t reg = DemangleSlot(slot, f);
1308 Method* m = f.GetMethod();
1309
1310 const VmapTable vmap_table(m->GetVmapTableRaw());
1311 uint32_t vmap_offset;
1312 if (vmap_table.IsInContext(reg, vmap_offset)) {
1313 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1314 }
Elliott Hughesdbb40792011-11-18 17:05:22 -08001315
1316 switch (tag) {
1317 case JDWP::JT_BOOLEAN:
1318 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001319 CHECK_EQ(width, 1U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001320 uint32_t intVal = f.GetVReg(m, reg);
1321 LOG(VERBOSE) << "get boolean local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001322 JDWP::Set1(buf+1, intVal != 0);
1323 }
1324 break;
1325 case JDWP::JT_BYTE:
1326 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001327 CHECK_EQ(width, 1U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001328 uint32_t intVal = f.GetVReg(m, reg);
1329 LOG(VERBOSE) << "get byte local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001330 JDWP::Set1(buf+1, intVal);
1331 }
1332 break;
1333 case JDWP::JT_SHORT:
1334 case JDWP::JT_CHAR:
1335 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001336 CHECK_EQ(width, 2U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001337 uint32_t intVal = f.GetVReg(m, reg);
1338 LOG(VERBOSE) << "get short/char local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001339 JDWP::Set2BE(buf+1, intVal);
1340 }
1341 break;
1342 case JDWP::JT_INT:
1343 case JDWP::JT_FLOAT:
1344 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001345 CHECK_EQ(width, 4U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001346 uint32_t intVal = f.GetVReg(m, reg);
1347 LOG(VERBOSE) << "get int/float local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001348 JDWP::Set4BE(buf+1, intVal);
1349 }
1350 break;
1351 case JDWP::JT_ARRAY:
1352 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001353 CHECK_EQ(width, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001354 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001355 LOG(VERBOSE) << "get array local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001356 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001357 LOG(FATAL) << "reg " << reg << " expected to hold array: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001358 }
1359 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1360 }
1361 break;
1362 case JDWP::JT_OBJECT:
1363 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001364 CHECK_EQ(width, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001365 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001366 LOG(VERBOSE) << "get object local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001367 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001368 LOG(FATAL) << "reg " << reg << " expected to hold object: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001369 }
1370 tag = TagFromObject(o);
1371 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1372 }
1373 break;
1374 case JDWP::JT_DOUBLE:
1375 case JDWP::JT_LONG:
1376 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001377 CHECK_EQ(width, 8U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001378 uint32_t lo = f.GetVReg(m, reg);
1379 uint64_t hi = f.GetVReg(m, reg + 1);
1380 uint64_t longVal = (hi << 32) | lo;
1381 LOG(VERBOSE) << "get double/long local " << hi << ":" << lo << " = " << longVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001382 JDWP::Set8BE(buf+1, longVal);
1383 }
1384 break;
1385 default:
1386 LOG(FATAL) << "unknown tag " << tag;
1387 break;
1388 }
1389
1390 // Prepend tag, which may have been updated.
1391 JDWP::Set1(buf, tag);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001392}
1393
Elliott Hughesdbb40792011-11-18 17:05:22 -08001394void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width) {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001395 Method** sp = reinterpret_cast<Method**>(frameId);
1396 Frame f;
1397 f.SetSP(sp);
1398 uint16_t reg = DemangleSlot(slot, f);
1399 Method* m = f.GetMethod();
1400
1401 const VmapTable vmap_table(m->GetVmapTableRaw());
1402 uint32_t vmap_offset;
1403 if (vmap_table.IsInContext(reg, vmap_offset)) {
1404 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1405 }
1406
1407 switch (tag) {
1408 case JDWP::JT_BOOLEAN:
1409 case JDWP::JT_BYTE:
1410 CHECK_EQ(width, 1U);
1411 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1412 break;
1413 case JDWP::JT_SHORT:
1414 case JDWP::JT_CHAR:
1415 CHECK_EQ(width, 2U);
1416 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1417 break;
1418 case JDWP::JT_INT:
1419 case JDWP::JT_FLOAT:
1420 CHECK_EQ(width, 4U);
1421 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1422 break;
1423 case JDWP::JT_ARRAY:
1424 case JDWP::JT_OBJECT:
1425 case JDWP::JT_STRING:
1426 {
1427 CHECK_EQ(width, sizeof(JDWP::ObjectId));
1428 Object* o = gRegistry->Get<Object*>(static_cast<JDWP::ObjectId>(value));
1429 f.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)));
1430 }
1431 break;
1432 case JDWP::JT_DOUBLE:
1433 case JDWP::JT_LONG:
1434 CHECK_EQ(width, 8U);
1435 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1436 f.SetVReg(m, reg + 1, static_cast<uint32_t>(value >> 32));
1437 break;
1438 default:
1439 LOG(FATAL) << "unknown tag " << tag;
1440 break;
1441 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001442}
1443
1444void Dbg::PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags) {
1445 UNIMPLEMENTED(FATAL);
1446}
1447
1448void Dbg::PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception) {
1449 UNIMPLEMENTED(FATAL);
1450}
1451
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001452void Dbg::PostClassPrepare(Class* c) {
1453 UNIMPLEMENTED(FATAL);
1454}
1455
1456bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
1457 UNIMPLEMENTED(FATAL);
1458 return false;
1459}
1460
1461void Dbg::UnwatchLocation(const JDWP::JdwpLocation* pLoc) {
1462 UNIMPLEMENTED(FATAL);
1463}
1464
1465bool Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) {
1466 UNIMPLEMENTED(FATAL);
1467 return false;
1468}
1469
1470void Dbg::UnconfigureStep(JDWP::ObjectId threadId) {
1471 UNIMPLEMENTED(FATAL);
1472}
1473
Elliott Hughesaed4be92011-12-02 16:16:23 -08001474JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptObj) {
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001475 UNIMPLEMENTED(FATAL);
1476 return JDWP::ERR_NONE;
1477}
1478
1479void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
1480 UNIMPLEMENTED(FATAL);
1481}
1482
1483void Dbg::RegisterObjectId(JDWP::ObjectId id) {
1484 UNIMPLEMENTED(FATAL);
1485}
1486
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001487/*
1488 * "buf" contains a full JDWP packet, possibly with multiple chunks. We
1489 * need to process each, accumulate the replies, and ship the whole thing
1490 * back.
1491 *
1492 * Returns "true" if we have a reply. The reply buffer is newly allocated,
1493 * and includes the chunk type/length, followed by the data.
1494 *
1495 * TODO: we currently assume that the request and reply include a single
1496 * chunk. If this becomes inconvenient we will need to adapt.
1497 */
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001498bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001499 CHECK_GE(dataLen, 0);
1500
1501 Thread* self = Thread::Current();
1502 JNIEnv* env = self->GetJniEnv();
1503
1504 static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
1505 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1506 static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
1507 "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
1508 static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
1509 static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
1510 static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
1511 static jfieldID type_fid = env->GetFieldID(Chunk_class, "type", "I");
1512
1513 // Create a byte[] corresponding to 'buf'.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001514 ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(dataLen));
1515 if (dataArray.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001516 LOG(WARNING) << "byte[] allocation failed: " << dataLen;
1517 env->ExceptionClear();
1518 return false;
1519 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001520 env->SetByteArrayRegion(dataArray.get(), 0, dataLen, reinterpret_cast<const jbyte*>(buf));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001521
1522 const int kChunkHdrLen = 8;
1523
1524 // Run through and find all chunks. [Currently just find the first.]
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001525 ScopedByteArrayRO contents(env, dataArray.get());
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001526 jint type = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[0]));
1527 jint length = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[4]));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001528 jint offset = kChunkHdrLen;
1529 if (offset + length > dataLen) {
1530 LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%d)", length, dataLen);
1531 return false;
1532 }
1533
1534 // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001535 ScopedLocalRef<jobject> chunk(env, env->CallStaticObjectMethod(DdmServer_class, dispatch_mid, type, dataArray.get(), offset, length));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001536 if (env->ExceptionCheck()) {
1537 LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
1538 env->ExceptionDescribe();
1539 env->ExceptionClear();
1540 return false;
1541 }
1542
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001543 if (chunk.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001544 return false;
1545 }
1546
1547 /*
1548 * Pull the pieces out of the chunk. We copy the results into a
1549 * newly-allocated buffer that the caller can free. We don't want to
1550 * continue using the Chunk object because nothing has a reference to it.
1551 *
1552 * We could avoid this by returning type/data/offset/length and having
1553 * the caller be aware of the object lifetime issues, but that
1554 * integrates the JDWP code more tightly into the VM, and doesn't work
1555 * if we have responses for multiple chunks.
1556 *
1557 * So we're pretty much stuck with copying data around multiple times.
1558 */
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001559 ScopedLocalRef<jbyteArray> replyData(env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk.get(), data_fid)));
1560 length = env->GetIntField(chunk.get(), length_fid);
1561 offset = env->GetIntField(chunk.get(), offset_fid);
1562 type = env->GetIntField(chunk.get(), type_fid);
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001563
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001564 LOG(VERBOSE) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
1565 if (length == 0 || replyData.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001566 return false;
1567 }
1568
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001569 jsize replyLength = env->GetArrayLength(replyData.get());
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001570 if (offset + length > replyLength) {
1571 LOG(WARNING) << StringPrintf("chunk off=%d len=%d exceeds reply array len %d", offset, length, replyLength);
1572 return false;
1573 }
1574
1575 uint8_t* reply = new uint8_t[length + kChunkHdrLen];
1576 if (reply == NULL) {
1577 LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
1578 return false;
1579 }
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001580 JDWP::Set4BE(reply + 0, type);
1581 JDWP::Set4BE(reply + 4, length);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001582 env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001583
1584 *pReplyBuf = reply;
1585 *pReplyLen = length + kChunkHdrLen;
1586
1587 LOG(VERBOSE) << StringPrintf("dvmHandleDdm returning type=%.4s buf=%p len=%d", (char*) reply, reply, length);
1588 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001589}
1590
Elliott Hughesa2155262011-11-16 16:26:58 -08001591void Dbg::DdmBroadcast(bool connect) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001592 LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
1593
1594 Thread* self = Thread::Current();
1595 if (self->GetState() != Thread::kRunnable) {
1596 LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
1597 /* try anyway? */
1598 }
1599
1600 JNIEnv* env = self->GetJniEnv();
1601 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1602 static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
1603 jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
1604 env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
1605 if (env->ExceptionCheck()) {
1606 LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
1607 env->ExceptionDescribe();
1608 env->ExceptionClear();
1609 }
1610}
1611
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001612void Dbg::DdmConnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001613 Dbg::DdmBroadcast(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001614}
1615
1616void Dbg::DdmDisconnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001617 Dbg::DdmBroadcast(false);
Elliott Hughes47fce012011-10-25 18:37:19 -07001618 gDdmThreadNotification = false;
1619}
1620
1621/*
Elliott Hughes82188472011-11-07 18:11:48 -08001622 * Send a notification when a thread starts, stops, or changes its name.
Elliott Hughes47fce012011-10-25 18:37:19 -07001623 *
1624 * Because we broadcast the full set of threads when the notifications are
1625 * first enabled, it's possible for "thread" to be actively executing.
1626 */
Elliott Hughes82188472011-11-07 18:11:48 -08001627void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001628 if (!gDdmThreadNotification) {
1629 return;
1630 }
1631
Elliott Hughes82188472011-11-07 18:11:48 -08001632 if (type == CHUNK_TYPE("THDE")) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001633 uint8_t buf[4];
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001634 JDWP::Set4BE(&buf[0], t->GetThinLockId());
Elliott Hughes47fce012011-10-25 18:37:19 -07001635 Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
Elliott Hughes82188472011-11-07 18:11:48 -08001636 } else {
1637 CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
1638 SirtRef<String> name(t->GetName());
1639 size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
1640 const jchar* chars = name->GetCharArray()->GetData();
1641
Elliott Hughes21f32d72011-11-09 17:44:13 -08001642 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001643 JDWP::Append4BE(bytes, t->GetThinLockId());
1644 JDWP::AppendUtf16BE(bytes, chars, char_count);
Elliott Hughes21f32d72011-11-09 17:44:13 -08001645 CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
1646 Dbg::DdmSendChunk(type, bytes);
Elliott Hughes47fce012011-10-25 18:37:19 -07001647 }
1648}
1649
Elliott Hughesa2155262011-11-16 16:26:58 -08001650static void DdmSendThreadStartCallback(Thread* t, void*) {
Elliott Hughes82188472011-11-07 18:11:48 -08001651 Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001652}
1653
1654void Dbg::DdmSetThreadNotification(bool enable) {
1655 // We lock the thread list to avoid sending duplicate events or missing
1656 // a thread change. We should be okay holding this lock while sending
1657 // the messages out. (We have to hold it while accessing a live thread.)
Elliott Hughesbbd9d832011-11-07 14:40:00 -08001658 ScopedThreadListLock thread_list_lock;
Elliott Hughes47fce012011-10-25 18:37:19 -07001659
1660 gDdmThreadNotification = enable;
1661 if (enable) {
Elliott Hughesbfe487b2011-10-26 15:48:55 -07001662 Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback, NULL);
Elliott Hughes47fce012011-10-25 18:37:19 -07001663 }
1664}
1665
Elliott Hughesa2155262011-11-16 16:26:58 -08001666void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001667 if (gDebuggerActive) {
1668 JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
Elliott Hughes82188472011-11-07 18:11:48 -08001669 gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001670 }
Elliott Hughes82188472011-11-07 18:11:48 -08001671 Dbg::DdmSendThreadNotification(t, type);
Elliott Hughes47fce012011-10-25 18:37:19 -07001672}
1673
1674void Dbg::PostThreadStart(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001675 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001676}
1677
1678void Dbg::PostThreadDeath(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001679 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001680}
1681
Elliott Hughes82188472011-11-07 18:11:48 -08001682void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001683 CHECK(buf != NULL);
1684 iovec vec[1];
1685 vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
1686 vec[0].iov_len = byte_count;
1687 Dbg::DdmSendChunkV(type, vec, 1);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001688}
1689
Elliott Hughes21f32d72011-11-09 17:44:13 -08001690void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
1691 DdmSendChunk(type, bytes.size(), &bytes[0]);
1692}
1693
Elliott Hughescccd84f2011-12-05 16:51:54 -08001694void Dbg::DdmSendChunkV(uint32_t type, const struct iovec* iov, int iov_count) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001695 if (gJdwpState == NULL) {
1696 LOG(VERBOSE) << "Debugger thread not active, ignoring DDM send: " << type;
1697 } else {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001698 gJdwpState->DdmSendChunkV(type, iov, iov_count);
Elliott Hughes3bb81562011-10-21 18:52:59 -07001699 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001700}
1701
Elliott Hughes767a1472011-10-26 18:49:02 -07001702int Dbg::DdmHandleHpifChunk(HpifWhen when) {
1703 if (when == HPIF_WHEN_NOW) {
Elliott Hughes7162ad92011-10-27 14:08:42 -07001704 DdmSendHeapInfo(when);
Elliott Hughes767a1472011-10-26 18:49:02 -07001705 return true;
1706 }
1707
1708 if (when != HPIF_WHEN_NEVER && when != HPIF_WHEN_NEXT_GC && when != HPIF_WHEN_EVERY_GC) {
1709 LOG(ERROR) << "invalid HpifWhen value: " << static_cast<int>(when);
1710 return false;
1711 }
1712
1713 gDdmHpifWhen = when;
1714 return true;
1715}
1716
1717bool Dbg::DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when, Dbg::HpsgWhat what, bool native) {
1718 if (when != HPSG_WHEN_NEVER && when != HPSG_WHEN_EVERY_GC) {
1719 LOG(ERROR) << "invalid HpsgWhen value: " << static_cast<int>(when);
1720 return false;
1721 }
1722
1723 if (what != HPSG_WHAT_MERGED_OBJECTS && what != HPSG_WHAT_DISTINCT_OBJECTS) {
1724 LOG(ERROR) << "invalid HpsgWhat value: " << static_cast<int>(what);
1725 return false;
1726 }
1727
1728 if (native) {
1729 gDdmNhsgWhen = when;
1730 gDdmNhsgWhat = what;
1731 } else {
1732 gDdmHpsgWhen = when;
1733 gDdmHpsgWhat = what;
1734 }
1735 return true;
1736}
1737
Elliott Hughes7162ad92011-10-27 14:08:42 -07001738void Dbg::DdmSendHeapInfo(HpifWhen reason) {
1739 // If there's a one-shot 'when', reset it.
1740 if (reason == gDdmHpifWhen) {
1741 if (gDdmHpifWhen == HPIF_WHEN_NEXT_GC) {
1742 gDdmHpifWhen = HPIF_WHEN_NEVER;
1743 }
1744 }
1745
1746 /*
1747 * Chunk HPIF (client --> server)
1748 *
1749 * Heap Info. General information about the heap,
1750 * suitable for a summary display.
1751 *
1752 * [u4]: number of heaps
1753 *
1754 * For each heap:
1755 * [u4]: heap ID
1756 * [u8]: timestamp in ms since Unix epoch
1757 * [u1]: capture reason (same as 'when' value from server)
1758 * [u4]: max heap size in bytes (-Xmx)
1759 * [u4]: current heap size in bytes
1760 * [u4]: current number of bytes allocated
1761 * [u4]: current number of objects allocated
1762 */
1763 uint8_t heap_count = 1;
Elliott Hughes21f32d72011-11-09 17:44:13 -08001764 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001765 JDWP::Append4BE(bytes, heap_count);
1766 JDWP::Append4BE(bytes, 1); // Heap id (bogus; we only have one heap).
1767 JDWP::Append8BE(bytes, MilliTime());
1768 JDWP::Append1BE(bytes, reason);
1769 JDWP::Append4BE(bytes, Heap::GetMaxMemory()); // Max allowed heap size in bytes.
1770 JDWP::Append4BE(bytes, Heap::GetTotalMemory()); // Current heap size in bytes.
1771 JDWP::Append4BE(bytes, Heap::GetBytesAllocated());
1772 JDWP::Append4BE(bytes, Heap::GetObjectsAllocated());
Elliott Hughes21f32d72011-11-09 17:44:13 -08001773 CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
1774 Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
Elliott Hughes767a1472011-10-26 18:49:02 -07001775}
1776
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001777enum HpsgSolidity {
1778 SOLIDITY_FREE = 0,
1779 SOLIDITY_HARD = 1,
1780 SOLIDITY_SOFT = 2,
1781 SOLIDITY_WEAK = 3,
1782 SOLIDITY_PHANTOM = 4,
1783 SOLIDITY_FINALIZABLE = 5,
1784 SOLIDITY_SWEEP = 6,
1785};
1786
1787enum HpsgKind {
1788 KIND_OBJECT = 0,
1789 KIND_CLASS_OBJECT = 1,
1790 KIND_ARRAY_1 = 2,
1791 KIND_ARRAY_2 = 3,
1792 KIND_ARRAY_4 = 4,
1793 KIND_ARRAY_8 = 5,
1794 KIND_UNKNOWN = 6,
1795 KIND_NATIVE = 7,
1796};
1797
1798#define HPSG_PARTIAL (1<<7)
1799#define HPSG_STATE(solidity, kind) ((uint8_t)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
1800
1801struct HeapChunkContext {
1802 std::vector<uint8_t> buf;
1803 uint8_t* p;
1804 uint8_t* pieceLenField;
1805 size_t totalAllocationUnits;
Elliott Hughes82188472011-11-07 18:11:48 -08001806 uint32_t type;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001807 bool merge;
1808 bool needHeader;
1809
1810 // Maximum chunk size. Obtain this from the formula:
1811 // (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
1812 HeapChunkContext(bool merge, bool native)
1813 : buf(16384 - 16),
1814 type(0),
1815 merge(merge) {
1816 Reset();
1817 if (native) {
1818 type = CHUNK_TYPE("NHSG");
1819 } else {
1820 type = merge ? CHUNK_TYPE("HPSG") : CHUNK_TYPE("HPSO");
1821 }
1822 }
1823
1824 ~HeapChunkContext() {
1825 if (p > &buf[0]) {
1826 Flush();
1827 }
1828 }
1829
1830 void EnsureHeader(const void* chunk_ptr) {
1831 if (!needHeader) {
1832 return;
1833 }
1834
1835 // Start a new HPSx chunk.
1836 JDWP::Write4BE(&p, 1); // Heap id (bogus; we only have one heap).
1837 JDWP::Write1BE(&p, 8); // Size of allocation unit, in bytes.
1838
1839 JDWP::Write4BE(&p, reinterpret_cast<uintptr_t>(chunk_ptr)); // virtual address of segment start.
1840 JDWP::Write4BE(&p, 0); // offset of this piece (relative to the virtual address).
1841 // [u4]: length of piece, in allocation units
1842 // We won't know this until we're done, so save the offset and stuff in a dummy value.
1843 pieceLenField = p;
1844 JDWP::Write4BE(&p, 0x55555555);
1845 needHeader = false;
1846 }
1847
1848 void Flush() {
1849 // Patch the "length of piece" field.
1850 CHECK_LE(&buf[0], pieceLenField);
1851 CHECK_LE(pieceLenField, p);
1852 JDWP::Set4BE(pieceLenField, totalAllocationUnits);
1853
1854 Dbg::DdmSendChunk(type, p - &buf[0], &buf[0]);
1855 Reset();
1856 }
1857
Elliott Hughesa2155262011-11-16 16:26:58 -08001858 static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
1859 reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
1860 }
1861
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001862 private:
Elliott Hughesa2155262011-11-16 16:26:58 -08001863 enum { ALLOCATION_UNIT_SIZE = 8 };
1864
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001865 void Reset() {
1866 p = &buf[0];
1867 totalAllocationUnits = 0;
1868 needHeader = true;
1869 pieceLenField = NULL;
1870 }
1871
Elliott Hughesa2155262011-11-16 16:26:58 -08001872 void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
1873 CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001874
Elliott Hughesa2155262011-11-16 16:26:58 -08001875 /* Make sure there's enough room left in the buffer.
1876 * We need to use two bytes for every fractional 256
1877 * allocation units used by the chunk.
1878 */
1879 {
1880 size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
1881 size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1882 if (bytesLeft < needed) {
1883 Flush();
1884 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001885
Elliott Hughesa2155262011-11-16 16:26:58 -08001886 bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1887 if (bytesLeft < needed) {
1888 LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
1889 return;
1890 }
1891 }
1892
1893 // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
1894 EnsureHeader(chunk_ptr);
1895
1896 // Determine the type of this chunk.
1897 // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
1898 // If it's the same, we should combine them.
1899 uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
1900
1901 // Write out the chunk description.
1902 chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
1903 totalAllocationUnits += chunk_len;
1904 while (chunk_len > 256) {
1905 *p++ = state | HPSG_PARTIAL;
1906 *p++ = 255; // length - 1
1907 chunk_len -= 256;
1908 }
1909 *p++ = state;
1910 *p++ = chunk_len - 1;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001911 }
1912
Elliott Hughesa2155262011-11-16 16:26:58 -08001913 uint8_t ExamineObject(const Object* o, bool is_native_heap) {
1914 if (o == NULL) {
1915 return HPSG_STATE(SOLIDITY_FREE, 0);
1916 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001917
Elliott Hughesa2155262011-11-16 16:26:58 -08001918 // It's an allocated chunk. Figure out what it is.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001919
Elliott Hughesa2155262011-11-16 16:26:58 -08001920 // If we're looking at the native heap, we'll just return
1921 // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
1922 if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
1923 return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
1924 }
1925
1926 Class* c = o->GetClass();
1927 if (c == NULL) {
1928 // The object was probably just created but hasn't been initialized yet.
1929 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1930 }
1931
1932 if (!Heap::IsHeapAddress(c)) {
1933 LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
1934 return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
1935 }
1936
1937 if (c->IsClassClass()) {
1938 return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
1939 }
1940
1941 if (c->IsArrayClass()) {
1942 if (o->IsObjectArray()) {
1943 return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1944 }
1945 switch (c->GetComponentSize()) {
1946 case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
1947 case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
1948 case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1949 case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
1950 }
1951 }
1952
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001953 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1954 }
1955
Elliott Hughesa2155262011-11-16 16:26:58 -08001956 DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
1957};
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001958
1959void Dbg::DdmSendHeapSegments(bool native) {
1960 Dbg::HpsgWhen when;
1961 Dbg::HpsgWhat what;
1962 if (!native) {
1963 when = gDdmHpsgWhen;
1964 what = gDdmHpsgWhat;
1965 } else {
1966 when = gDdmNhsgWhen;
1967 what = gDdmNhsgWhat;
1968 }
1969 if (when == HPSG_WHEN_NEVER) {
1970 return;
1971 }
1972
1973 // Figure out what kind of chunks we'll be sending.
1974 CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
1975
1976 // First, send a heap start chunk.
1977 uint8_t heap_id[4];
1978 JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
1979 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
1980
1981 // Send a series of heap segment chunks.
Elliott Hughesa2155262011-11-16 16:26:58 -08001982 HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
1983 if (native) {
1984 dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
1985 } else {
1986 Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
1987 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001988
1989 // Finally, send a heap end chunk.
1990 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
Elliott Hughes767a1472011-10-26 18:49:02 -07001991}
1992
Elliott Hughes545a0642011-11-08 19:10:03 -08001993void Dbg::SetAllocTrackingEnabled(bool enabled) {
1994 MutexLock mu(gAllocTrackerLock);
1995 if (enabled) {
1996 if (recent_allocation_records_ == NULL) {
1997 LOG(INFO) << "Enabling alloc tracker (" << kNumAllocRecords << " entries, "
1998 << kMaxAllocRecordStackDepth << " frames --> "
1999 << (sizeof(AllocRecord) * kNumAllocRecords) << " bytes)";
2000 gAllocRecordHead = gAllocRecordCount = 0;
2001 recent_allocation_records_ = new AllocRecord[kNumAllocRecords];
2002 CHECK(recent_allocation_records_ != NULL);
2003 }
2004 } else {
2005 delete[] recent_allocation_records_;
2006 recent_allocation_records_ = NULL;
2007 }
2008}
2009
2010struct AllocRecordStackVisitor : public Thread::StackVisitor {
2011 AllocRecordStackVisitor(AllocRecord* record) : record(record), depth(0) {
2012 }
2013
2014 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
2015 if (depth >= kMaxAllocRecordStackDepth) {
2016 return;
2017 }
2018 Method* m = f.GetMethod();
2019 if (m == NULL || m->IsCalleeSaveMethod()) {
2020 return;
2021 }
2022 record->stack[depth].method = m;
2023 record->stack[depth].raw_pc = pc;
2024 ++depth;
2025 }
2026
2027 ~AllocRecordStackVisitor() {
2028 // Clear out any unused stack trace elements.
2029 for (; depth < kMaxAllocRecordStackDepth; ++depth) {
2030 record->stack[depth].method = NULL;
2031 record->stack[depth].raw_pc = 0;
2032 }
2033 }
2034
2035 AllocRecord* record;
2036 size_t depth;
2037};
2038
2039void Dbg::RecordAllocation(Class* type, size_t byte_count) {
2040 Thread* self = Thread::Current();
2041 CHECK(self != NULL);
2042
2043 MutexLock mu(gAllocTrackerLock);
2044 if (recent_allocation_records_ == NULL) {
2045 return;
2046 }
2047
2048 // Advance and clip.
2049 if (++gAllocRecordHead == kNumAllocRecords) {
2050 gAllocRecordHead = 0;
2051 }
2052
2053 // Fill in the basics.
2054 AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
2055 record->type = type;
2056 record->byte_count = byte_count;
2057 record->thin_lock_id = self->GetThinLockId();
2058
2059 // Fill in the stack trace.
2060 AllocRecordStackVisitor visitor(record);
2061 self->WalkStack(&visitor);
2062
2063 if (gAllocRecordCount < kNumAllocRecords) {
2064 ++gAllocRecordCount;
2065 }
2066}
2067
2068/*
2069 * Return the index of the head element.
2070 *
2071 * We point at the most-recently-written record, so if allocRecordCount is 1
2072 * we want to use the current element. Take "head+1" and subtract count
2073 * from it.
2074 *
2075 * We need to handle underflow in our circular buffer, so we add
2076 * kNumAllocRecords and then mask it back down.
2077 */
2078inline static int headIndex() {
2079 return (gAllocRecordHead+1 + kNumAllocRecords - gAllocRecordCount) & (kNumAllocRecords-1);
2080}
2081
2082void Dbg::DumpRecentAllocations() {
2083 MutexLock mu(gAllocTrackerLock);
2084 if (recent_allocation_records_ == NULL) {
2085 LOG(INFO) << "Not recording tracked allocations";
2086 return;
2087 }
2088
2089 // "i" is the head of the list. We want to start at the end of the
2090 // list and move forward to the tail.
2091 size_t i = headIndex();
2092 size_t count = gAllocRecordCount;
2093
2094 LOG(INFO) << "Tracked allocations, (head=" << gAllocRecordHead << " count=" << count << ")";
2095 while (count--) {
2096 AllocRecord* record = &recent_allocation_records_[i];
2097
2098 LOG(INFO) << StringPrintf(" T=%-2d %6d ", record->thin_lock_id, record->byte_count)
2099 << PrettyClass(record->type);
2100
2101 for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
2102 const Method* m = record->stack[stack_frame].method;
2103 if (m == NULL) {
2104 break;
2105 }
2106 LOG(INFO) << " " << PrettyMethod(m) << " line " << record->stack[stack_frame].LineNumber();
2107 }
2108
2109 // pause periodically to help logcat catch up
2110 if ((count % 5) == 0) {
2111 usleep(40000);
2112 }
2113
2114 i = (i + 1) & (kNumAllocRecords-1);
2115 }
2116}
2117
2118class StringTable {
2119 public:
2120 StringTable() {
2121 }
2122
2123 void Add(const String* s) {
2124 table_.insert(s);
2125 }
2126
2127 size_t IndexOf(const String* s) {
2128 return std::distance(table_.begin(), table_.find(s));
2129 }
2130
2131 size_t Size() {
2132 return table_.size();
2133 }
2134
2135 void WriteTo(std::vector<uint8_t>& bytes) {
2136 typedef std::set<const String*>::const_iterator It; // TODO: C++0x auto
2137 for (It it = table_.begin(); it != table_.end(); ++it) {
2138 const String* s = *it;
2139 JDWP::AppendUtf16BE(bytes, s->GetCharArray()->GetData(), s->GetLength());
2140 }
2141 }
2142
2143 private:
2144 std::set<const String*> table_;
2145 DISALLOW_COPY_AND_ASSIGN(StringTable);
2146};
2147
2148/*
2149 * The data we send to DDMS contains everything we have recorded.
2150 *
2151 * Message header (all values big-endian):
2152 * (1b) message header len (to allow future expansion); includes itself
2153 * (1b) entry header len
2154 * (1b) stack frame len
2155 * (2b) number of entries
2156 * (4b) offset to string table from start of message
2157 * (2b) number of class name strings
2158 * (2b) number of method name strings
2159 * (2b) number of source file name strings
2160 * For each entry:
2161 * (4b) total allocation size
2162 * (2b) threadId
2163 * (2b) allocated object's class name index
2164 * (1b) stack depth
2165 * For each stack frame:
2166 * (2b) method's class name
2167 * (2b) method name
2168 * (2b) method source file
2169 * (2b) line number, clipped to 32767; -2 if native; -1 if no source
2170 * (xb) class name strings
2171 * (xb) method name strings
2172 * (xb) source file strings
2173 *
2174 * As with other DDM traffic, strings are sent as a 4-byte length
2175 * followed by UTF-16 data.
2176 *
2177 * We send up 16-bit unsigned indexes into string tables. In theory there
2178 * can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
2179 * each table, but in practice there should be far fewer.
2180 *
2181 * The chief reason for using a string table here is to keep the size of
2182 * the DDMS message to a minimum. This is partly to make the protocol
2183 * efficient, but also because we have to form the whole thing up all at
2184 * once in a memory buffer.
2185 *
2186 * We use separate string tables for class names, method names, and source
2187 * files to keep the indexes small. There will generally be no overlap
2188 * between the contents of these tables.
2189 */
2190jbyteArray Dbg::GetRecentAllocations() {
2191 if (false) {
2192 DumpRecentAllocations();
2193 }
2194
2195 MutexLock mu(gAllocTrackerLock);
2196
2197 /*
2198 * Part 1: generate string tables.
2199 */
2200 StringTable class_names;
2201 StringTable method_names;
2202 StringTable filenames;
2203
2204 int count = gAllocRecordCount;
2205 int idx = headIndex();
2206 while (count--) {
2207 AllocRecord* record = &recent_allocation_records_[idx];
2208
2209 class_names.Add(record->type->GetDescriptor());
2210
2211 for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
2212 const Method* m = record->stack[i].method;
2213 if (m != NULL) {
2214 class_names.Add(m->GetDeclaringClass()->GetDescriptor());
2215 method_names.Add(m->GetName());
2216 filenames.Add(m->GetDeclaringClass()->GetSourceFile());
2217 }
2218 }
2219
2220 idx = (idx + 1) & (kNumAllocRecords-1);
2221 }
2222
2223 LOG(INFO) << "allocation records: " << gAllocRecordCount;
2224
2225 /*
2226 * Part 2: allocate a buffer and generate the output.
2227 */
2228 std::vector<uint8_t> bytes;
2229
2230 // (1b) message header len (to allow future expansion); includes itself
2231 // (1b) entry header len
2232 // (1b) stack frame len
2233 const int kMessageHeaderLen = 15;
2234 const int kEntryHeaderLen = 9;
2235 const int kStackFrameLen = 8;
2236 JDWP::Append1BE(bytes, kMessageHeaderLen);
2237 JDWP::Append1BE(bytes, kEntryHeaderLen);
2238 JDWP::Append1BE(bytes, kStackFrameLen);
2239
2240 // (2b) number of entries
2241 // (4b) offset to string table from start of message
2242 // (2b) number of class name strings
2243 // (2b) number of method name strings
2244 // (2b) number of source file name strings
2245 JDWP::Append2BE(bytes, gAllocRecordCount);
2246 size_t string_table_offset = bytes.size();
2247 JDWP::Append4BE(bytes, 0); // We'll patch this later...
2248 JDWP::Append2BE(bytes, class_names.Size());
2249 JDWP::Append2BE(bytes, method_names.Size());
2250 JDWP::Append2BE(bytes, filenames.Size());
2251
2252 count = gAllocRecordCount;
2253 idx = headIndex();
2254 while (count--) {
2255 // For each entry:
2256 // (4b) total allocation size
2257 // (2b) thread id
2258 // (2b) allocated object's class name index
2259 // (1b) stack depth
2260 AllocRecord* record = &recent_allocation_records_[idx];
2261 size_t stack_depth = record->GetDepth();
2262 JDWP::Append4BE(bytes, record->byte_count);
2263 JDWP::Append2BE(bytes, record->thin_lock_id);
2264 JDWP::Append2BE(bytes, class_names.IndexOf(record->type->GetDescriptor()));
2265 JDWP::Append1BE(bytes, stack_depth);
2266
2267 for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
2268 // For each stack frame:
2269 // (2b) method's class name
2270 // (2b) method name
2271 // (2b) method source file
2272 // (2b) line number, clipped to 32767; -2 if native; -1 if no source
2273 const Method* m = record->stack[stack_frame].method;
2274 JDWP::Append2BE(bytes, class_names.IndexOf(m->GetDeclaringClass()->GetDescriptor()));
2275 JDWP::Append2BE(bytes, method_names.IndexOf(m->GetName()));
2276 JDWP::Append2BE(bytes, filenames.IndexOf(m->GetDeclaringClass()->GetSourceFile()));
2277 JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
2278 }
2279
2280 idx = (idx + 1) & (kNumAllocRecords-1);
2281 }
2282
2283 // (xb) class name strings
2284 // (xb) method name strings
2285 // (xb) source file strings
2286 JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
2287 class_names.WriteTo(bytes);
2288 method_names.WriteTo(bytes);
2289 filenames.WriteTo(bytes);
2290
2291 JNIEnv* env = Thread::Current()->GetJniEnv();
2292 jbyteArray result = env->NewByteArray(bytes.size());
2293 if (result != NULL) {
2294 env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
2295 }
2296 return result;
2297}
2298
Elliott Hughes872d4ec2011-10-21 17:07:15 -07002299} // namespace art