blob: 98d501c9f4657adb2e02ed1a53aa37d43552e970 [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) {
155 if (c->IsArrayClass()) {
156 return JDWP::JT_ARRAY;
157 }
158
159 if (c->IsStringClass()) {
160 return JDWP::JT_STRING;
161 } else if (c->IsClassClass()) {
162 return JDWP::JT_CLASS_OBJECT;
163#if 0 // TODO
164 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
165 return JDWP::JT_THREAD;
166 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
167 return JDWP::JT_THREAD_GROUP;
168 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
169 return JDWP::JT_CLASS_LOADER;
170#endif
171 } else {
172 return JDWP::JT_OBJECT;
173 }
174}
175
176/*
177 * Objects declared to hold Object might actually hold a more specific
178 * type. The debugger may take a special interest in these (e.g. it
179 * wants to display the contents of Strings), so we want to return an
180 * appropriate tag.
181 *
182 * Null objects are tagged JT_OBJECT.
183 */
184static JDWP::JdwpTag TagFromObject(const Object* o) {
185 return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
186}
187
188static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
189 switch (tag) {
190 case JDWP::JT_BOOLEAN:
191 case JDWP::JT_BYTE:
192 case JDWP::JT_CHAR:
193 case JDWP::JT_FLOAT:
194 case JDWP::JT_DOUBLE:
195 case JDWP::JT_INT:
196 case JDWP::JT_LONG:
197 case JDWP::JT_SHORT:
198 case JDWP::JT_VOID:
199 return true;
200 default:
201 return false;
202 }
203}
204
Elliott Hughes3bb81562011-10-21 18:52:59 -0700205/*
206 * Handle one of the JDWP name/value pairs.
207 *
208 * JDWP options are:
209 * help: if specified, show help message and bail
210 * transport: may be dt_socket or dt_shmem
211 * address: for dt_socket, "host:port", or just "port" when listening
212 * server: if "y", wait for debugger to attach; if "n", attach to debugger
213 * timeout: how long to wait for debugger to connect / listen
214 *
215 * Useful with server=n (these aren't supported yet):
216 * onthrow=<exception-name>: connect to debugger when exception thrown
217 * onuncaught=y|n: connect to debugger when uncaught exception thrown
218 * launch=<command-line>: launch the debugger itself
219 *
220 * The "transport" option is required, as is "address" if server=n.
221 */
222static bool ParseJdwpOption(const std::string& name, const std::string& value) {
223 if (name == "transport") {
224 if (value == "dt_socket") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700225 gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700226 } else if (value == "dt_android_adb") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700227 gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700228 } else {
229 LOG(ERROR) << "JDWP transport not supported: " << value;
230 return false;
231 }
232 } else if (name == "server") {
233 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700234 gJdwpOptions.server = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700235 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700236 gJdwpOptions.server = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700237 } else {
238 LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
239 return false;
240 }
241 } else if (name == "suspend") {
242 if (value == "n") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700243 gJdwpOptions.suspend = false;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700244 } else if (value == "y") {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700245 gJdwpOptions.suspend = true;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700246 } else {
247 LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
248 return false;
249 }
250 } else if (name == "address") {
251 /* this is either <port> or <host>:<port> */
252 std::string port_string;
Elliott Hughes376a7a02011-10-24 18:35:55 -0700253 gJdwpOptions.host.clear();
Elliott Hughes3bb81562011-10-21 18:52:59 -0700254 std::string::size_type colon = value.find(':');
255 if (colon != std::string::npos) {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700256 gJdwpOptions.host = value.substr(0, colon);
Elliott Hughes3bb81562011-10-21 18:52:59 -0700257 port_string = value.substr(colon + 1);
258 } else {
259 port_string = value;
260 }
261 if (port_string.empty()) {
262 LOG(ERROR) << "JDWP address missing port: " << value;
263 return false;
264 }
265 char* end;
266 long port = strtol(port_string.c_str(), &end, 10);
267 if (*end != '\0') {
268 LOG(ERROR) << "JDWP address has junk in port field: " << value;
269 return false;
270 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700271 gJdwpOptions.port = port;
Elliott Hughes3bb81562011-10-21 18:52:59 -0700272 } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
273 /* valid but unsupported */
274 LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
275 } else {
276 LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
277 }
278
279 return true;
280}
281
282/*
283 * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
284 * "transport=dt_socket,address=8000,server=y,suspend=n"
285 */
286bool Dbg::ParseJdwpOptions(const std::string& options) {
Elliott Hughes47fce012011-10-25 18:37:19 -0700287 LOG(VERBOSE) << "ParseJdwpOptions: " << options;
288
Elliott Hughes3bb81562011-10-21 18:52:59 -0700289 std::vector<std::string> pairs;
290 Split(options, ',', pairs);
291
292 for (size_t i = 0; i < pairs.size(); ++i) {
293 std::string::size_type equals = pairs[i].find('=');
294 if (equals == std::string::npos) {
295 LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
296 return false;
297 }
298 ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
299 }
300
Elliott Hughes376a7a02011-10-24 18:35:55 -0700301 if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700302 LOG(ERROR) << "Must specify JDWP transport: " << options;
303 }
Elliott Hughes376a7a02011-10-24 18:35:55 -0700304 if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700305 LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
306 return false;
307 }
308
309 gJdwpConfigured = true;
310 return true;
311}
312
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700313void Dbg::StartJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700314 if (!gJdwpAllowed || !gJdwpConfigured) {
315 // No JDWP for you!
316 return;
317 }
318
Elliott Hughes475fc232011-10-25 15:00:35 -0700319 CHECK(gRegistry == NULL);
320 gRegistry = new ObjectRegistry;
321
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700322 // Init JDWP if the debugger is enabled. This may connect out to a
323 // debugger, passively listen for a debugger, or block waiting for a
324 // debugger.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700325 gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
326 if (gJdwpState == NULL) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -0800327 // We probably failed because some other process has the port already, which means that
328 // if we don't abort the user is likely to think they're talking to us when they're actually
329 // talking to that other process.
330 LOG(FATAL) << "debugger thread failed to initialize";
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700331 }
332
333 // If a debugger has already attached, send the "welcome" message.
334 // This may cause us to suspend all threads.
Elliott Hughes376a7a02011-10-24 18:35:55 -0700335 if (gJdwpState->IsActive()) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800336 //ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Elliott Hughes376a7a02011-10-24 18:35:55 -0700337 if (!gJdwpState->PostVMStart()) {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700338 LOG(WARNING) << "failed to post 'start' message to debugger";
339 }
340 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700341}
342
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700343void Dbg::StopJdwp() {
Elliott Hughes376a7a02011-10-24 18:35:55 -0700344 delete gJdwpState;
Elliott Hughes475fc232011-10-25 15:00:35 -0700345 delete gRegistry;
346 gRegistry = NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700347}
348
Elliott Hughes767a1472011-10-26 18:49:02 -0700349void Dbg::GcDidFinish() {
350 if (gDdmHpifWhen != HPIF_WHEN_NEVER) {
351 LOG(DEBUG) << "Sending VM heap info to DDM";
Elliott Hughes7162ad92011-10-27 14:08:42 -0700352 DdmSendHeapInfo(gDdmHpifWhen);
Elliott Hughes767a1472011-10-26 18:49:02 -0700353 }
354 if (gDdmHpsgWhen != HPSG_WHEN_NEVER) {
355 LOG(DEBUG) << "Dumping VM heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700356 DdmSendHeapSegments(false);
Elliott Hughes767a1472011-10-26 18:49:02 -0700357 }
358 if (gDdmNhsgWhen != HPSG_WHEN_NEVER) {
359 LOG(DEBUG) << "Dumping native heap to DDM";
Elliott Hughes6a5bd492011-10-28 14:33:57 -0700360 DdmSendHeapSegments(true);
Elliott Hughes767a1472011-10-26 18:49:02 -0700361 }
362}
363
Elliott Hughes4ffd3132011-10-24 12:06:42 -0700364void Dbg::SetJdwpAllowed(bool allowed) {
365 gJdwpAllowed = allowed;
366}
367
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700368DebugInvokeReq* Dbg::GetInvokeReq() {
Elliott Hughes475fc232011-10-25 15:00:35 -0700369 return Thread::Current()->GetInvokeReq();
370}
371
372Thread* Dbg::GetDebugThread() {
373 return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
374}
375
376void Dbg::ClearWaitForEventThread() {
377 gJdwpState->ClearWaitForEventThread();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700378}
379
380void Dbg::Connected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700381 CHECK(!gDebuggerConnected);
382 LOG(VERBOSE) << "JDWP has attached";
383 gDebuggerConnected = true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700384}
385
Elliott Hughesa2155262011-11-16 16:26:58 -0800386void Dbg::GoActive() {
387 // Enable all debugging features, including scans for breakpoints.
388 // This is a no-op if we're already active.
389 // Only called from the JDWP handler thread.
390 if (gDebuggerActive) {
391 return;
392 }
393
394 LOG(INFO) << "Debugger is active";
395
396 // TODO: CHECK we don't have any outstanding breakpoints.
397
398 gDebuggerActive = true;
399
400 //dvmEnableAllSubMode(kSubModeDebuggerActive);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700401}
402
403void Dbg::Disconnected() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700404 CHECK(gDebuggerConnected);
405
406 gDebuggerActive = false;
407
408 //dvmDisableAllSubMode(kSubModeDebuggerActive);
409
410 gRegistry->Clear();
411 gDebuggerConnected = false;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700412}
413
414bool Dbg::IsDebuggerConnected() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700415 return gDebuggerActive;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700416}
417
418bool Dbg::IsDebuggingEnabled() {
Elliott Hughes3bb81562011-10-21 18:52:59 -0700419 return gJdwpConfigured;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700420}
421
422int64_t Dbg::LastDebuggerActivity() {
Elliott Hughesca951522011-12-05 12:01:32 -0800423 return gJdwpState->LastDebuggerActivity();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700424}
425
426int Dbg::ThreadRunning() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700427 return static_cast<int>(Thread::Current()->SetState(Thread::kRunnable));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700428}
429
430int Dbg::ThreadWaiting() {
Elliott Hughesd1cc8362011-10-24 16:58:50 -0700431 return static_cast<int>(Thread::Current()->SetState(Thread::kVmWait));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700432}
433
Elliott Hughes6ba581a2011-10-25 11:45:35 -0700434int Dbg::ThreadContinuing(int new_state) {
435 return static_cast<int>(Thread::Current()->SetState(static_cast<Thread::State>(new_state)));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700436}
437
438void Dbg::UndoDebuggerSuspensions() {
Elliott Hughes234ab152011-10-26 14:02:26 -0700439 Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700440}
441
442void Dbg::Exit(int status) {
Elliott Hughes1bba14f2011-12-01 18:00:36 -0800443 exit(status); // This is all dalvik did.
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700444}
445
Elliott Hughesbfe487b2011-10-26 15:48:55 -0700446void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
447 if (gRegistry != NULL) {
448 gRegistry->VisitRoots(visitor, arg);
449 }
450}
451
Elliott Hughesa2155262011-11-16 16:26:58 -0800452std::string Dbg::GetClassDescriptor(JDWP::RefTypeId classId) {
453 Class* c = gRegistry->Get<Class*>(classId);
454 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700455}
456
457JDWP::ObjectId Dbg::GetClassObject(JDWP::RefTypeId id) {
458 UNIMPLEMENTED(FATAL);
459 return 0;
460}
461
462JDWP::RefTypeId Dbg::GetSuperclass(JDWP::RefTypeId id) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800463 Class* c = gRegistry->Get<Class*>(id);
464 return gRegistry->Add(c->GetSuperClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700465}
466
467JDWP::ObjectId Dbg::GetClassLoader(JDWP::RefTypeId id) {
Elliott Hughes1bba14f2011-12-01 18:00:36 -0800468 Object* o = gRegistry->Get<Object*>(id);
469 return gRegistry->Add(o->GetClass()->GetClassLoader());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700470}
471
472uint32_t Dbg::GetAccessFlags(JDWP::RefTypeId id) {
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800473 Class* c = gRegistry->Get<Class*>(id);
474 return c->GetAccessFlags() & kAccJavaFlagsMask;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700475}
476
Elliott Hughesaed4be92011-12-02 16:16:23 -0800477bool Dbg::IsInterface(JDWP::RefTypeId classId) {
478 Class* c = gRegistry->Get<Class*>(classId);
479 return c->IsInterface();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700480}
481
Elliott Hughesa2155262011-11-16 16:26:58 -0800482void Dbg::GetClassList(uint32_t* pClassCount, JDWP::RefTypeId** pClasses) {
483 // Get the complete list of reference classes (i.e. all classes except
484 // the primitive types).
485 // Returns a newly-allocated buffer full of RefTypeId values.
486 struct ClassListCreator {
487 static bool Visit(Class* c, void* arg) {
488 return reinterpret_cast<ClassListCreator*>(arg)->Visit(c);
489 }
490
491 bool Visit(Class* c) {
492 if (!c->IsPrimitive()) {
493 classes.push_back(static_cast<JDWP::RefTypeId>(gRegistry->Add(c)));
494 }
495 return true;
496 }
497
498 std::vector<JDWP::RefTypeId> classes;
499 };
500
501 ClassListCreator clc;
502 Runtime::Current()->GetClassLinker()->VisitClasses(ClassListCreator::Visit, &clc);
503 *pClassCount = clc.classes.size();
504 *pClasses = new JDWP::RefTypeId[clc.classes.size()];
505 for (size_t i = 0; i < clc.classes.size(); ++i) {
506 (*pClasses)[i] = clc.classes[i];
507 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700508}
509
510void Dbg::GetVisibleClassList(JDWP::ObjectId classLoaderId, uint32_t* pNumClasses, JDWP::RefTypeId** pClassRefBuf) {
511 UNIMPLEMENTED(FATAL);
512}
513
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800514void Dbg::GetClassInfo(JDWP::RefTypeId classId, JDWP::JdwpTypeTag* pTypeTag, uint32_t* pStatus, std::string* pDescriptor) {
Elliott Hughesa2155262011-11-16 16:26:58 -0800515 Class* c = gRegistry->Get<Class*>(classId);
516 if (c->IsArrayClass()) {
517 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
518 *pTypeTag = JDWP::TT_ARRAY;
519 } else {
520 if (c->IsErroneous()) {
521 *pStatus = JDWP::CS_ERROR;
522 } else {
523 *pStatus = JDWP::CS_VERIFIED | JDWP::CS_PREPARED | JDWP::CS_INITIALIZED;
524 }
525 *pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
526 }
527
528 if (pDescriptor != NULL) {
529 *pDescriptor = c->GetDescriptor()->ToModifiedUtf8();
530 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700531}
532
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800533void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids) {
534 std::vector<Class*> classes;
535 Runtime::Current()->GetClassLinker()->LookupClasses(descriptor, classes);
536 ids.clear();
537 for (size_t i = 0; i < classes.size(); ++i) {
538 ids.push_back(gRegistry->Add(classes[i]));
539 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700540}
541
Elliott Hughes6fa602d2011-12-02 17:54:25 -0800542void Dbg::GetObjectType(JDWP::ObjectId objectId, JDWP::JdwpTypeTag* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
Elliott Hughes499c5132011-11-17 14:55:11 -0800543 Object* o = gRegistry->Get<Object*>(objectId);
544 if (o->GetClass()->IsArrayClass()) {
545 *pRefTypeTag = JDWP::TT_ARRAY;
546 } else if (o->GetClass()->IsInterface()) {
547 *pRefTypeTag = JDWP::TT_INTERFACE;
548 } else {
549 *pRefTypeTag = JDWP::TT_CLASS;
550 }
551 *pRefTypeId = gRegistry->Add(o->GetClass());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700552}
553
554uint8_t Dbg::GetClassObjectType(JDWP::RefTypeId refTypeId) {
555 UNIMPLEMENTED(FATAL);
556 return 0;
557}
558
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800559std::string Dbg::GetSignature(JDWP::RefTypeId refTypeId) {
560 Class* c = gRegistry->Get<Class*>(refTypeId);
561 CHECK(c != NULL);
562 return c->GetDescriptor()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700563}
564
Elliott Hughes03181a82011-11-17 17:22:21 -0800565bool Dbg::GetSourceFile(JDWP::RefTypeId refTypeId, std::string& result) {
566 Class* c = gRegistry->Get<Class*>(refTypeId);
567 CHECK(c != NULL);
568
569 String* source_file = c->GetSourceFile();
570 if (source_file == NULL) {
571 return false;
572 }
573 result = source_file->ToModifiedUtf8();
574 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700575}
576
577const char* Dbg::GetObjectTypeName(JDWP::ObjectId objectId) {
578 UNIMPLEMENTED(FATAL);
579 return NULL;
580}
581
582uint8_t Dbg::GetObjectTag(JDWP::ObjectId objectId) {
Elliott Hughes24437992011-11-30 14:49:33 -0800583 Object* o = gRegistry->Get<Object*>(objectId);
584 return TagFromObject(o);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700585}
586
Elliott Hughesaed4be92011-12-02 16:16:23 -0800587size_t Dbg::GetTagWidth(JDWP::JdwpTag tag) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800588 switch (tag) {
589 case JDWP::JT_VOID:
590 return 0;
591 case JDWP::JT_BYTE:
592 case JDWP::JT_BOOLEAN:
593 return 1;
594 case JDWP::JT_CHAR:
595 case JDWP::JT_SHORT:
596 return 2;
597 case JDWP::JT_FLOAT:
598 case JDWP::JT_INT:
599 return 4;
600 case JDWP::JT_ARRAY:
601 case JDWP::JT_OBJECT:
602 case JDWP::JT_STRING:
603 case JDWP::JT_THREAD:
604 case JDWP::JT_THREAD_GROUP:
605 case JDWP::JT_CLASS_LOADER:
606 case JDWP::JT_CLASS_OBJECT:
607 return sizeof(JDWP::ObjectId);
608 case JDWP::JT_DOUBLE:
609 case JDWP::JT_LONG:
610 return 8;
611 default:
612 LOG(FATAL) << "unknown tag " << tag;
613 return -1;
614 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700615}
616
617int Dbg::GetArrayLength(JDWP::ObjectId arrayId) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800618 Object* o = gRegistry->Get<Object*>(arrayId);
619 Array* a = o->AsArray();
620 return a->GetLength();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700621}
622
623uint8_t Dbg::GetArrayElementTag(JDWP::ObjectId arrayId) {
Elliott Hughes24437992011-11-30 14:49:33 -0800624 Object* o = gRegistry->Get<Object*>(arrayId);
625 Array* a = o->AsArray();
626 std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
627 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
628 if (!IsPrimitiveTag(tag)) {
629 tag = TagFromClass(a->GetClass()->GetComponentType());
630 }
631 return tag;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700632}
633
Elliott Hughes24437992011-11-30 14:49:33 -0800634bool Dbg::OutputArray(JDWP::ObjectId arrayId, int offset, int count, JDWP::ExpandBuf* pReply) {
635 Object* o = gRegistry->Get<Object*>(arrayId);
636 Array* a = o->AsArray();
637
638 if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
639 LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
640 return false;
641 }
642
643 std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
644 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
645
646 if (IsPrimitiveTag(tag)) {
647 size_t width = GetTagWidth(tag);
648 const uint8_t* src = reinterpret_cast<uint8_t*>(a->GetRawData());
649 uint8_t* dst = expandBufAddSpace(pReply, count * width);
650 if (width == 8) {
651 const uint64_t* src8 = reinterpret_cast<const uint64_t*>(src);
652 for (int i = 0; i < count; ++i) JDWP::Write8BE(&dst, src8[offset + i]);
653 } else if (width == 4) {
654 const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
655 for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[offset + i]);
656 } else if (width == 2) {
657 const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
658 for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[offset + i]);
659 } else {
660 memcpy(dst, &src[offset * width], count * width);
661 }
662 } else {
663 ObjectArray<Object>* oa = a->AsObjectArray<Object>();
664 for (int i = 0; i < count; ++i) {
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800665 Object* element = oa->Get(offset + i);
Elliott Hughes24437992011-11-30 14:49:33 -0800666 JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
667 expandBufAdd1(pReply, specific_tag);
668 expandBufAddObjectId(pReply, gRegistry->Add(element));
669 }
670 }
671
672 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700673}
674
Elliott Hughesf03b8f62011-12-02 14:26:25 -0800675bool Dbg::SetArrayElements(JDWP::ObjectId arrayId, int offset, int count, const uint8_t* src) {
676 Object* o = gRegistry->Get<Object*>(arrayId);
677 Array* a = o->AsArray();
678
679 if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
680 LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
681 return false;
682 }
683
684 std::string descriptor(a->GetClass()->GetDescriptor()->ToModifiedUtf8());
685 JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
686
687 if (IsPrimitiveTag(tag)) {
688 size_t width = GetTagWidth(tag);
689 uint8_t* dst = &(reinterpret_cast<uint8_t*>(a->GetRawData())[offset * width]);
690 if (width == 8) {
691 for (int i = 0; i < count; ++i) {
692 // Handle potentially non-aligned memory access one byte at a time for ARM's benefit.
693 uint64_t value;
694 for (size_t j = 0; j < sizeof(uint64_t); ++j) reinterpret_cast<uint8_t*>(&value)[j] = src[j];
695 src += sizeof(uint64_t);
696 JDWP::Write8BE(&dst, value);
697 }
698 } else if (width == 4) {
699 const uint32_t* src4 = reinterpret_cast<const uint32_t*>(src);
700 for (int i = 0; i < count; ++i) JDWP::Write4BE(&dst, src4[i]);
701 } else if (width == 2) {
702 const uint16_t* src2 = reinterpret_cast<const uint16_t*>(src);
703 for (int i = 0; i < count; ++i) JDWP::Write2BE(&dst, src2[i]);
704 } else {
705 memcpy(&dst[offset * width], src, count * width);
706 }
707 } else {
708 ObjectArray<Object>* oa = a->AsObjectArray<Object>();
709 for (int i = 0; i < count; ++i) {
710 JDWP::ObjectId id = JDWP::ReadObjectId(&src);
711 oa->Set(offset + i, gRegistry->Get<Object*>(id));
712 }
713 }
714
715 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700716}
717
718JDWP::ObjectId Dbg::CreateString(const char* str) {
Elliott Hughescccd84f2011-12-05 16:51:54 -0800719 return gRegistry->Add(String::AllocFromModifiedUtf8(str));
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700720}
721
722JDWP::ObjectId Dbg::CreateObject(JDWP::RefTypeId classId) {
Elliott Hughescccd84f2011-12-05 16:51:54 -0800723 Class* c = gRegistry->Get<Class*>(classId);
724 return gRegistry->Add(c->AllocObject());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700725}
726
727JDWP::ObjectId Dbg::CreateArrayObject(JDWP::RefTypeId arrayTypeId, uint32_t length) {
728 UNIMPLEMENTED(FATAL);
729 return 0;
730}
731
732bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
733 UNIMPLEMENTED(FATAL);
734 return false;
735}
736
Elliott Hughes03181a82011-11-17 17:22:21 -0800737JDWP::FieldId ToFieldId(Field* f) {
738#ifdef MOVING_GARBAGE_COLLECTOR
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700739 UNIMPLEMENTED(FATAL);
Elliott Hughes03181a82011-11-17 17:22:21 -0800740#else
741 return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f));
742#endif
743}
744
745JDWP::MethodId ToMethodId(Method* m) {
746#ifdef MOVING_GARBAGE_COLLECTOR
747 UNIMPLEMENTED(FATAL);
748#else
749 return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m));
750#endif
751}
752
Elliott Hughesaed4be92011-12-02 16:16:23 -0800753Field* FromFieldId(JDWP::FieldId fid) {
754#ifdef MOVING_GARBAGE_COLLECTOR
755 UNIMPLEMENTED(FATAL);
756#else
757 return reinterpret_cast<Field*>(static_cast<uintptr_t>(fid));
758#endif
759}
760
Elliott Hughes03181a82011-11-17 17:22:21 -0800761Method* FromMethodId(JDWP::MethodId mid) {
762#ifdef MOVING_GARBAGE_COLLECTOR
763 UNIMPLEMENTED(FATAL);
764#else
765 return reinterpret_cast<Method*>(static_cast<uintptr_t>(mid));
766#endif
767}
768
769std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
770 return FromMethodId(methodId)->GetName()->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700771}
772
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800773/*
774 * Augment the access flags for synthetic methods and fields by setting
775 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
776 * flags not specified by the Java programming language.
777 */
778static uint32_t MangleAccessFlags(uint32_t accessFlags) {
779 accessFlags &= kAccJavaFlagsMask;
780 if ((accessFlags & kAccSynthetic) != 0) {
781 accessFlags |= 0xf0000000;
782 }
783 return accessFlags;
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700784}
785
Elliott Hughesdbb40792011-11-18 17:05:22 -0800786static const uint16_t kEclipseWorkaroundSlot = 1000;
787
788/*
789 * Eclipse appears to expect that the "this" reference is in slot zero.
790 * If it's not, the "variables" display will show two copies of "this",
791 * possibly because it gets "this" from SF.ThisObject and then displays
792 * all locals with nonzero slot numbers.
793 *
794 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
795 * SF.GetValues / SF.SetValues we map them back.
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800796 *
797 * TODO: jdb uses the value to determine whether a variable is a local or an argument,
798 * by checking whether it's less than the number of arguments. To make that work, we'd
799 * have to "mangle" all the arguments to come first, not just the implicit argument 'this'.
Elliott Hughesdbb40792011-11-18 17:05:22 -0800800 */
801static uint16_t MangleSlot(uint16_t slot, const char* name) {
802 uint16_t newSlot = slot;
803 if (strcmp(name, "this") == 0) {
804 newSlot = 0;
805 } else if (slot == 0) {
806 newSlot = kEclipseWorkaroundSlot;
807 }
808 return newSlot;
809}
810
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800811static uint16_t DemangleSlot(uint16_t slot, Frame& f) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800812 if (slot == kEclipseWorkaroundSlot) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800813 return 0;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800814 } else if (slot == 0) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800815 Method* m = f.GetMethod();
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800816 return m->NumRegisters() - m->NumIns();
Elliott Hughesdbb40792011-11-18 17:05:22 -0800817 }
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800818 return slot;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800819}
820
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800821void Dbg::OutputDeclaredFields(JDWP::RefTypeId refTypeId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800822 Class* c = gRegistry->Get<Class*>(refTypeId);
823 CHECK(c != NULL);
824
825 size_t instance_field_count = c->NumInstanceFields();
826 size_t static_field_count = c->NumStaticFields();
827
828 expandBufAdd4BE(pReply, instance_field_count + static_field_count);
829
830 for (size_t i = 0; i < instance_field_count + static_field_count; ++i) {
831 Field* f = (i < instance_field_count) ? c->GetInstanceField(i) : c->GetStaticField(i - instance_field_count);
832
833 expandBufAddFieldId(pReply, ToFieldId(f));
834 expandBufAddUtf8String(pReply, f->GetName()->ToModifiedUtf8().c_str());
835 expandBufAddUtf8String(pReply, f->GetTypeDescriptor());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800836 if (with_generic) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800837 static const char genericSignature[1] = "";
838 expandBufAddUtf8String(pReply, genericSignature);
839 }
840 expandBufAdd4BE(pReply, MangleAccessFlags(f->GetAccessFlags()));
841 }
842}
843
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800844void Dbg::OutputDeclaredMethods(JDWP::RefTypeId refTypeId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800845 Class* c = gRegistry->Get<Class*>(refTypeId);
846 CHECK(c != NULL);
847
848 size_t direct_method_count = c->NumDirectMethods();
849 size_t virtual_method_count = c->NumVirtualMethods();
850
851 expandBufAdd4BE(pReply, direct_method_count + virtual_method_count);
852
853 for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) {
854 Method* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count);
855
856 expandBufAddMethodId(pReply, ToMethodId(m));
857 expandBufAddUtf8String(pReply, m->GetName()->ToModifiedUtf8().c_str());
858 expandBufAddUtf8String(pReply, m->GetSignature()->ToModifiedUtf8().c_str());
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800859 if (with_generic) {
Elliott Hughesa2e54f62011-11-17 13:01:30 -0800860 static const char genericSignature[1] = "";
861 expandBufAddUtf8String(pReply, genericSignature);
862 }
863 expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags()));
864 }
865}
866
867void Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId refTypeId, JDWP::ExpandBuf* pReply) {
868 Class* c = gRegistry->Get<Class*>(refTypeId);
869 CHECK(c != NULL);
870 size_t interface_count = c->NumInterfaces();
871 expandBufAdd4BE(pReply, interface_count);
872 for (size_t i = 0; i < interface_count; ++i) {
873 expandBufAddRefTypeId(pReply, gRegistry->Add(c->GetInterface(i)));
874 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700875}
876
877void Dbg::OutputLineTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, JDWP::ExpandBuf* pReply) {
Elliott Hughes03181a82011-11-17 17:22:21 -0800878 struct DebugCallbackContext {
879 int numItems;
880 JDWP::ExpandBuf* pReply;
881
882 static bool Callback(void* context, uint32_t address, uint32_t lineNum) {
883 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
884 expandBufAdd8BE(pContext->pReply, address);
885 expandBufAdd4BE(pContext->pReply, lineNum);
886 pContext->numItems++;
887 return true;
888 }
889 };
890
891 Method* m = FromMethodId(methodId);
892 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
893 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
894 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
895
896 uint64_t start, end;
897 if (m->IsNative()) {
898 start = -1;
899 end = -1;
900 } else {
901 start = 0;
902 end = code_item->insns_size_in_code_units_; // TODO: what are the units supposed to be? *2?
903 }
904
905 expandBufAdd8BE(pReply, start);
906 expandBufAdd8BE(pReply, end);
907
908 // Add numLines later
909 size_t numLinesOffset = expandBufGetLength(pReply);
910 expandBufAdd4BE(pReply, 0);
911
912 DebugCallbackContext context;
913 context.numItems = 0;
914 context.pReply = pReply;
915
916 dex_file.DecodeDebugInfo(code_item, m, DebugCallbackContext::Callback, NULL, &context);
917
918 JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700919}
920
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800921void Dbg::OutputVariableTable(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId, bool with_generic, JDWP::ExpandBuf* pReply) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800922 struct DebugCallbackContext {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800923 JDWP::ExpandBuf* pReply;
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800924 size_t variable_count;
925 bool with_generic;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800926
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800927 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 -0800928 DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
929
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800930 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 -0800931
Elliott Hughes68fdbd02011-11-29 19:22:47 -0800932 slot = MangleSlot(slot, name);
933
Elliott Hughesdbb40792011-11-18 17:05:22 -0800934 expandBufAdd8BE(pContext->pReply, startAddress);
935 expandBufAddUtf8String(pContext->pReply, name);
936 expandBufAddUtf8String(pContext->pReply, descriptor);
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800937 if (pContext->with_generic) {
Elliott Hughesdbb40792011-11-18 17:05:22 -0800938 expandBufAddUtf8String(pContext->pReply, signature);
939 }
940 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
941 expandBufAdd4BE(pContext->pReply, slot);
942
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800943 ++pContext->variable_count;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800944 }
945 };
946
947 Method* m = FromMethodId(methodId);
948 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
949 const DexFile& dex_file = class_linker->FindDexFile(m->GetDeclaringClass()->GetDexCache());
950 const DexFile::CodeItem* code_item = dex_file.GetCodeItem(m->GetCodeItemOffset());
951
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800952 // arg_count considers doubles and longs to take 2 units.
953 // variable_count considers everything to take 1 unit.
954 std::string shorty(m->GetShorty()->ToModifiedUtf8());
955 expandBufAdd4BE(pReply, m->NumArgRegisters(shorty));
Elliott Hughesdbb40792011-11-18 17:05:22 -0800956
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800957 // We don't know the total number of variables yet, so leave a blank and update it later.
958 size_t variable_count_offset = expandBufGetLength(pReply);
Elliott Hughesdbb40792011-11-18 17:05:22 -0800959 expandBufAdd4BE(pReply, 0);
960
961 DebugCallbackContext context;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800962 context.pReply = pReply;
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800963 context.variable_count = 0;
964 context.with_generic = with_generic;
Elliott Hughesdbb40792011-11-18 17:05:22 -0800965
966 dex_file.DecodeDebugInfo(code_item, m, NULL, DebugCallbackContext::Callback, &context);
967
Elliott Hughesc5b734a2011-12-01 17:20:58 -0800968 JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700969}
970
Elliott Hughesaed4be92011-12-02 16:16:23 -0800971JDWP::JdwpTag Dbg::GetFieldBasicTag(JDWP::FieldId fieldId) {
972 return BasicTagFromDescriptor(FromFieldId(fieldId)->GetTypeDescriptor());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700973}
974
Elliott Hughesaed4be92011-12-02 16:16:23 -0800975JDWP::JdwpTag Dbg::GetStaticFieldBasicTag(JDWP::FieldId fieldId) {
976 return BasicTagFromDescriptor(FromFieldId(fieldId)->GetTypeDescriptor());
Elliott Hughes872d4ec2011-10-21 17:07:15 -0700977}
978
979void Dbg::GetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
Elliott Hughesaed4be92011-12-02 16:16:23 -0800980 Object* o = gRegistry->Get<Object*>(objectId);
981 Field* f = FromFieldId(fieldId);
982
983 JDWP::JdwpTag tag = BasicTagFromDescriptor(f->GetTypeDescriptor());
984
985 if (IsPrimitiveTag(tag)) {
986 expandBufAdd1(pReply, tag);
987 if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) {
988 expandBufAdd1(pReply, f->Get32(o));
989 } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) {
990 expandBufAdd2BE(pReply, f->Get32(o));
991 } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) {
992 expandBufAdd4BE(pReply, f->Get32(o));
993 } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
994 expandBufAdd8BE(pReply, f->Get64(o));
995 } else {
996 LOG(FATAL) << "unknown tag: " << tag;
997 }
998 } else {
999 Object* value = f->GetObject(o);
1000 expandBufAdd1(pReply, TagFromObject(value));
1001 expandBufAddObjectId(pReply, gRegistry->Add(value));
1002 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001003}
1004
1005void Dbg::SetFieldValue(JDWP::ObjectId objectId, JDWP::FieldId fieldId, uint64_t value, int width) {
Elliott Hughesaed4be92011-12-02 16:16:23 -08001006 Object* o = gRegistry->Get<Object*>(objectId);
1007 Field* f = FromFieldId(fieldId);
1008
1009 JDWP::JdwpTag tag = BasicTagFromDescriptor(f->GetTypeDescriptor());
1010
1011 if (IsPrimitiveTag(tag)) {
1012 if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) {
1013 f->Set64(o, value);
1014 } else {
1015 f->Set32(o, value);
1016 }
1017 } else {
1018 f->SetObject(o, gRegistry->Get<Object*>(value));
1019 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001020}
1021
Elliott Hughes6fa602d2011-12-02 17:54:25 -08001022void Dbg::GetStaticFieldValue(JDWP::FieldId fieldId, JDWP::ExpandBuf* pReply) {
1023 GetFieldValue(0, fieldId, pReply);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001024}
1025
Elliott Hughes6fa602d2011-12-02 17:54:25 -08001026void Dbg::SetStaticFieldValue(JDWP::FieldId fieldId, uint64_t value, int width) {
1027 SetFieldValue(0, fieldId, value, width);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001028}
1029
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001030std::string Dbg::StringToUtf8(JDWP::ObjectId strId) {
1031 String* s = gRegistry->Get<String*>(strId);
1032 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001033}
1034
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001035Thread* DecodeThread(JDWP::ObjectId threadId) {
1036 Object* thread_peer = gRegistry->Get<Object*>(threadId);
1037 CHECK(thread_peer != NULL);
1038 return Thread::FromManagedThread(thread_peer);
1039}
1040
1041bool Dbg::GetThreadName(JDWP::ObjectId threadId, std::string& name) {
1042 ScopedThreadListLock thread_list_lock;
1043 Thread* thread = DecodeThread(threadId);
1044 if (thread == NULL) {
1045 return false;
1046 }
1047 StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetName()->ToModifiedUtf8().c_str());
1048 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001049}
1050
1051JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
Elliott Hughes499c5132011-11-17 14:55:11 -08001052 Object* thread = gRegistry->Get<Object*>(threadId);
1053 CHECK(thread != NULL);
1054
1055 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
1056 CHECK(c != NULL);
1057 Field* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
1058 CHECK(f != NULL);
1059 Object* group = f->GetObject(thread);
1060 CHECK(group != NULL);
1061 return gRegistry->Add(group);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001062}
1063
Elliott Hughes499c5132011-11-17 14:55:11 -08001064std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
1065 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
1066 CHECK(thread_group != NULL);
1067
1068 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1069 CHECK(c != NULL);
1070 Field* f = c->FindInstanceField("name", "Ljava/lang/String;");
1071 CHECK(f != NULL);
1072 String* s = reinterpret_cast<String*>(f->GetObject(thread_group));
1073 return s->ToModifiedUtf8();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001074}
1075
1076JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001077 Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
1078 CHECK(thread_group != NULL);
1079
1080 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1081 CHECK(c != NULL);
1082 Field* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
1083 CHECK(f != NULL);
1084 Object* parent = f->GetObject(thread_group);
1085 return gRegistry->Add(parent);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001086}
1087
Elliott Hughes499c5132011-11-17 14:55:11 -08001088static Object* GetStaticThreadGroup(const char* field_name) {
1089 Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
1090 CHECK(c != NULL);
1091 Field* f = c->FindStaticField(field_name, "Ljava/lang/ThreadGroup;");
1092 CHECK(f != NULL);
1093 Object* group = f->GetObject(NULL);
1094 CHECK(group != NULL);
1095 return group;
1096}
1097
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001098JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -08001099 return gRegistry->Add(GetStaticThreadGroup("mSystem"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001100}
1101
1102JDWP::ObjectId Dbg::GetMainThreadGroupId() {
Elliott Hughes499c5132011-11-17 14:55:11 -08001103 return gRegistry->Add(GetStaticThreadGroup("mMain"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001104}
1105
Elliott Hughes499c5132011-11-17 14:55:11 -08001106bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* pThreadStatus, uint32_t* pSuspendStatus) {
1107 ScopedThreadListLock thread_list_lock;
1108
1109 Thread* thread = DecodeThread(threadId);
1110 if (thread == NULL) {
1111 return false;
1112 }
1113
1114 switch (thread->GetState()) {
1115 case Thread::kTerminated: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1116 case Thread::kRunnable: *pThreadStatus = JDWP::TS_RUNNING; break;
1117 case Thread::kTimedWaiting: *pThreadStatus = JDWP::TS_SLEEPING; break;
1118 case Thread::kBlocked: *pThreadStatus = JDWP::TS_MONITOR; break;
1119 case Thread::kWaiting: *pThreadStatus = JDWP::TS_WAIT; break;
1120 case Thread::kInitializing: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1121 case Thread::kStarting: *pThreadStatus = JDWP::TS_ZOMBIE; break;
1122 case Thread::kNative: *pThreadStatus = JDWP::TS_RUNNING; break;
1123 case Thread::kVmWait: *pThreadStatus = JDWP::TS_WAIT; break;
1124 case Thread::kSuspended: *pThreadStatus = JDWP::TS_RUNNING; break;
1125 default:
1126 LOG(FATAL) << "unknown thread state " << thread->GetState();
1127 }
1128
1129 *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : 0);
1130
1131 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001132}
1133
1134uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
1135 UNIMPLEMENTED(FATAL);
1136 return 0;
1137}
1138
1139bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -08001140 return DecodeThread(threadId) != NULL;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001141}
1142
1143bool Dbg::IsSuspended(JDWP::ObjectId threadId) {
Elliott Hughes761928d2011-11-16 18:33:03 -08001144 return DecodeThread(threadId)->IsSuspended();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001145}
1146
1147//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
1148
Elliott Hughesa2155262011-11-16 16:26:58 -08001149void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
1150 struct ThreadListVisitor {
1151 static void Visit(Thread* t, void* arg) {
1152 reinterpret_cast<ThreadListVisitor*>(arg)->Visit(t);
1153 }
1154
1155 void Visit(Thread* t) {
1156 if (t == Dbg::GetDebugThread()) {
1157 // Skip the JDWP thread. Some debuggers get bent out of shape when they can't suspend and
1158 // query all threads, so it's easier if we just don't tell them about this thread.
1159 return;
1160 }
1161 if (thread_group == NULL || t->GetThreadGroup() == thread_group) {
1162 threads.push_back(gRegistry->Add(t->GetPeer()));
1163 }
1164 }
1165
1166 Object* thread_group;
1167 std::vector<JDWP::ObjectId> threads;
1168 };
1169
1170 ThreadListVisitor tlv;
1171 tlv.thread_group = thread_group;
1172
1173 {
1174 ScopedThreadListLock thread_list_lock;
1175 Runtime::Current()->GetThreadList()->ForEach(ThreadListVisitor::Visit, &tlv);
1176 }
1177
1178 *pThreadCount = tlv.threads.size();
1179 if (*pThreadCount == 0) {
1180 *ppThreadIds = NULL;
1181 } else {
1182 *ppThreadIds = new JDWP::ObjectId[*pThreadCount];
1183 for (size_t i = 0; i < *pThreadCount; ++i) {
1184 (*ppThreadIds)[i] = tlv.threads[i];
1185 }
1186 }
1187}
1188
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001189void Dbg::GetThreadGroupThreads(JDWP::ObjectId threadGroupId, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001190 GetThreadGroupThreadsImpl(gRegistry->Get<Object*>(threadGroupId), ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001191}
1192
1193void Dbg::GetAllThreads(JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001194 GetThreadGroupThreadsImpl(NULL, ppThreadIds, pThreadCount);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001195}
1196
1197int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
Elliott Hughes03181a82011-11-17 17:22:21 -08001198 ScopedThreadListLock thread_list_lock;
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001199 struct CountStackDepthVisitor : public Thread::StackVisitor {
1200 CountStackDepthVisitor() : depth(0) {}
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001201 virtual void VisitFrame(const Frame& f, uintptr_t) {
1202 // TODO: we'll need to skip callee-save frames too.
1203 if (f.HasMethod()) {
1204 ++depth;
1205 }
Elliott Hughesa2e54f62011-11-17 13:01:30 -08001206 }
1207 size_t depth;
1208 };
1209 CountStackDepthVisitor visitor;
1210 DecodeThread(threadId)->WalkStack(&visitor);
1211 return visitor.depth;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001212}
1213
Elliott Hughes03181a82011-11-17 17:22:21 -08001214bool Dbg::GetThreadFrame(JDWP::ObjectId threadId, int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc) {
1215 ScopedThreadListLock thread_list_lock;
1216 struct GetFrameVisitor : public Thread::StackVisitor {
1217 GetFrameVisitor(int desired_frame_number, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc)
1218 : found(false) ,depth(0), desired_frame_number(desired_frame_number), pFrameId(pFrameId), pLoc(pLoc) {
1219 }
1220 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001221 // TODO: we'll need to skip callee-save frames too.
Elliott Hughes03181a82011-11-17 17:22:21 -08001222 if (!f.HasMethod()) {
Elliott Hughesf8a2df72011-12-01 12:19:54 -08001223 return; // The debugger can't do anything useful with a frame that has no Method*.
Elliott Hughes03181a82011-11-17 17:22:21 -08001224 }
1225
1226 if (depth == desired_frame_number) {
1227 *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
1228
1229 Method* m = f.GetMethod();
1230 Class* c = m->GetDeclaringClass();
1231
1232 pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
1233 pLoc->classId = gRegistry->Add(c);
1234 pLoc->methodId = ToMethodId(m);
1235 pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
1236
1237 found = true;
1238 }
1239 ++depth;
1240 }
1241 bool found;
1242 int depth;
1243 int desired_frame_number;
1244 JDWP::FrameId* pFrameId;
1245 JDWP::JdwpLocation* pLoc;
1246 };
1247 GetFrameVisitor visitor(desired_frame_number, pFrameId, pLoc);
1248 visitor.desired_frame_number = desired_frame_number;
1249 DecodeThread(threadId)->WalkStack(&visitor);
1250 return visitor.found;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001251}
1252
1253JDWP::ObjectId Dbg::GetThreadSelfId() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001254 return gRegistry->Add(Thread::Current()->GetPeer());
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001255}
1256
Elliott Hughes475fc232011-10-25 15:00:35 -07001257void Dbg::SuspendVM() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001258 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 -07001259 Runtime::Current()->GetThreadList()->SuspendAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001260}
1261
1262void Dbg::ResumeVM() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001263 Runtime::Current()->GetThreadList()->ResumeAll(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001264}
1265
1266void Dbg::SuspendThread(JDWP::ObjectId threadId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001267 Object* peer = gRegistry->Get<Object*>(threadId);
1268 ScopedThreadListLock thread_list_lock;
1269 Thread* thread = Thread::FromManagedThread(peer);
1270 if (thread == NULL) {
1271 LOG(WARNING) << "No such thread for suspend: " << peer;
1272 return;
1273 }
1274 Runtime::Current()->GetThreadList()->Suspend(thread, true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001275}
1276
1277void Dbg::ResumeThread(JDWP::ObjectId threadId) {
Elliott Hughes4e235312011-12-02 11:34:15 -08001278 Object* peer = gRegistry->Get<Object*>(threadId);
1279 ScopedThreadListLock thread_list_lock;
1280 Thread* thread = Thread::FromManagedThread(peer);
1281 if (thread == NULL) {
1282 LOG(WARNING) << "No such thread for resume: " << peer;
1283 return;
1284 }
1285 Runtime::Current()->GetThreadList()->Resume(thread, true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001286}
1287
1288void Dbg::SuspendSelf() {
Elliott Hughes475fc232011-10-25 15:00:35 -07001289 Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001290}
1291
1292bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
1293 UNIMPLEMENTED(FATAL);
1294 return false;
1295}
1296
Elliott Hughescccd84f2011-12-05 16:51:54 -08001297void 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 -08001298 Method** sp = reinterpret_cast<Method**>(frameId);
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001299 Frame f;
1300 f.SetSP(sp);
1301 uint16_t reg = DemangleSlot(slot, f);
1302 Method* m = f.GetMethod();
1303
1304 const VmapTable vmap_table(m->GetVmapTableRaw());
1305 uint32_t vmap_offset;
1306 if (vmap_table.IsInContext(reg, vmap_offset)) {
1307 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1308 }
Elliott Hughesdbb40792011-11-18 17:05:22 -08001309
1310 switch (tag) {
1311 case JDWP::JT_BOOLEAN:
1312 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001313 CHECK_EQ(width, 1U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001314 uint32_t intVal = f.GetVReg(m, reg);
1315 LOG(VERBOSE) << "get boolean local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001316 JDWP::Set1(buf+1, intVal != 0);
1317 }
1318 break;
1319 case JDWP::JT_BYTE:
1320 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001321 CHECK_EQ(width, 1U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001322 uint32_t intVal = f.GetVReg(m, reg);
1323 LOG(VERBOSE) << "get byte local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001324 JDWP::Set1(buf+1, intVal);
1325 }
1326 break;
1327 case JDWP::JT_SHORT:
1328 case JDWP::JT_CHAR:
1329 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001330 CHECK_EQ(width, 2U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001331 uint32_t intVal = f.GetVReg(m, reg);
1332 LOG(VERBOSE) << "get short/char local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001333 JDWP::Set2BE(buf+1, intVal);
1334 }
1335 break;
1336 case JDWP::JT_INT:
1337 case JDWP::JT_FLOAT:
1338 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001339 CHECK_EQ(width, 4U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001340 uint32_t intVal = f.GetVReg(m, reg);
1341 LOG(VERBOSE) << "get int/float local " << reg << " = " << intVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001342 JDWP::Set4BE(buf+1, intVal);
1343 }
1344 break;
1345 case JDWP::JT_ARRAY:
1346 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001347 CHECK_EQ(width, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001348 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001349 LOG(VERBOSE) << "get array local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001350 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001351 LOG(FATAL) << "reg " << reg << " expected to hold array: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001352 }
1353 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1354 }
1355 break;
1356 case JDWP::JT_OBJECT:
1357 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001358 CHECK_EQ(width, sizeof(JDWP::ObjectId));
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001359 Object* o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001360 LOG(VERBOSE) << "get object local " << reg << " = " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001361 if (o != NULL && !Heap::IsHeapAddress(o)) {
Elliott Hughes68fdbd02011-11-29 19:22:47 -08001362 LOG(FATAL) << "reg " << reg << " expected to hold object: " << o;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001363 }
1364 tag = TagFromObject(o);
1365 JDWP::SetObjectId(buf+1, gRegistry->Add(o));
1366 }
1367 break;
1368 case JDWP::JT_DOUBLE:
1369 case JDWP::JT_LONG:
1370 {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001371 CHECK_EQ(width, 8U);
Elliott Hughes1bba14f2011-12-01 18:00:36 -08001372 uint32_t lo = f.GetVReg(m, reg);
1373 uint64_t hi = f.GetVReg(m, reg + 1);
1374 uint64_t longVal = (hi << 32) | lo;
1375 LOG(VERBOSE) << "get double/long local " << hi << ":" << lo << " = " << longVal;
Elliott Hughesdbb40792011-11-18 17:05:22 -08001376 JDWP::Set8BE(buf+1, longVal);
1377 }
1378 break;
1379 default:
1380 LOG(FATAL) << "unknown tag " << tag;
1381 break;
1382 }
1383
1384 // Prepend tag, which may have been updated.
1385 JDWP::Set1(buf, tag);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001386}
1387
Elliott Hughesdbb40792011-11-18 17:05:22 -08001388void 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 -08001389 Method** sp = reinterpret_cast<Method**>(frameId);
1390 Frame f;
1391 f.SetSP(sp);
1392 uint16_t reg = DemangleSlot(slot, f);
1393 Method* m = f.GetMethod();
1394
1395 const VmapTable vmap_table(m->GetVmapTableRaw());
1396 uint32_t vmap_offset;
1397 if (vmap_table.IsInContext(reg, vmap_offset)) {
1398 UNIMPLEMENTED(FATAL) << "don't know how to pull locals from callee save frames: " << vmap_offset;
1399 }
1400
1401 switch (tag) {
1402 case JDWP::JT_BOOLEAN:
1403 case JDWP::JT_BYTE:
1404 CHECK_EQ(width, 1U);
1405 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1406 break;
1407 case JDWP::JT_SHORT:
1408 case JDWP::JT_CHAR:
1409 CHECK_EQ(width, 2U);
1410 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1411 break;
1412 case JDWP::JT_INT:
1413 case JDWP::JT_FLOAT:
1414 CHECK_EQ(width, 4U);
1415 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1416 break;
1417 case JDWP::JT_ARRAY:
1418 case JDWP::JT_OBJECT:
1419 case JDWP::JT_STRING:
1420 {
1421 CHECK_EQ(width, sizeof(JDWP::ObjectId));
1422 Object* o = gRegistry->Get<Object*>(static_cast<JDWP::ObjectId>(value));
1423 f.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)));
1424 }
1425 break;
1426 case JDWP::JT_DOUBLE:
1427 case JDWP::JT_LONG:
1428 CHECK_EQ(width, 8U);
1429 f.SetVReg(m, reg, static_cast<uint32_t>(value));
1430 f.SetVReg(m, reg + 1, static_cast<uint32_t>(value >> 32));
1431 break;
1432 default:
1433 LOG(FATAL) << "unknown tag " << tag;
1434 break;
1435 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001436}
1437
1438void Dbg::PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags) {
1439 UNIMPLEMENTED(FATAL);
1440}
1441
1442void Dbg::PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception) {
1443 UNIMPLEMENTED(FATAL);
1444}
1445
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001446void Dbg::PostClassPrepare(Class* c) {
1447 UNIMPLEMENTED(FATAL);
1448}
1449
1450bool Dbg::WatchLocation(const JDWP::JdwpLocation* pLoc) {
1451 UNIMPLEMENTED(FATAL);
1452 return false;
1453}
1454
1455void Dbg::UnwatchLocation(const JDWP::JdwpLocation* pLoc) {
1456 UNIMPLEMENTED(FATAL);
1457}
1458
1459bool Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) {
1460 UNIMPLEMENTED(FATAL);
1461 return false;
1462}
1463
1464void Dbg::UnconfigureStep(JDWP::ObjectId threadId) {
1465 UNIMPLEMENTED(FATAL);
1466}
1467
Elliott Hughesaed4be92011-12-02 16:16:23 -08001468JDWP::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 -07001469 UNIMPLEMENTED(FATAL);
1470 return JDWP::ERR_NONE;
1471}
1472
1473void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
1474 UNIMPLEMENTED(FATAL);
1475}
1476
1477void Dbg::RegisterObjectId(JDWP::ObjectId id) {
1478 UNIMPLEMENTED(FATAL);
1479}
1480
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001481/*
1482 * "buf" contains a full JDWP packet, possibly with multiple chunks. We
1483 * need to process each, accumulate the replies, and ship the whole thing
1484 * back.
1485 *
1486 * Returns "true" if we have a reply. The reply buffer is newly allocated,
1487 * and includes the chunk type/length, followed by the data.
1488 *
1489 * TODO: we currently assume that the request and reply include a single
1490 * chunk. If this becomes inconvenient we will need to adapt.
1491 */
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001492bool Dbg::DdmHandlePacket(const uint8_t* buf, int dataLen, uint8_t** pReplyBuf, int* pReplyLen) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001493 CHECK_GE(dataLen, 0);
1494
1495 Thread* self = Thread::Current();
1496 JNIEnv* env = self->GetJniEnv();
1497
1498 static jclass Chunk_class = env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk");
1499 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1500 static jmethodID dispatch_mid = env->GetStaticMethodID(DdmServer_class, "dispatch",
1501 "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
1502 static jfieldID data_fid = env->GetFieldID(Chunk_class, "data", "[B");
1503 static jfieldID length_fid = env->GetFieldID(Chunk_class, "length", "I");
1504 static jfieldID offset_fid = env->GetFieldID(Chunk_class, "offset", "I");
1505 static jfieldID type_fid = env->GetFieldID(Chunk_class, "type", "I");
1506
1507 // Create a byte[] corresponding to 'buf'.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001508 ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(dataLen));
1509 if (dataArray.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001510 LOG(WARNING) << "byte[] allocation failed: " << dataLen;
1511 env->ExceptionClear();
1512 return false;
1513 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001514 env->SetByteArrayRegion(dataArray.get(), 0, dataLen, reinterpret_cast<const jbyte*>(buf));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001515
1516 const int kChunkHdrLen = 8;
1517
1518 // Run through and find all chunks. [Currently just find the first.]
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001519 ScopedByteArrayRO contents(env, dataArray.get());
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001520 jint type = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[0]));
1521 jint length = JDWP::Get4BE(reinterpret_cast<const uint8_t*>(&contents[4]));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001522 jint offset = kChunkHdrLen;
1523 if (offset + length > dataLen) {
1524 LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%d)", length, dataLen);
1525 return false;
1526 }
1527
1528 // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001529 ScopedLocalRef<jobject> chunk(env, env->CallStaticObjectMethod(DdmServer_class, dispatch_mid, type, dataArray.get(), offset, length));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001530 if (env->ExceptionCheck()) {
1531 LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
1532 env->ExceptionDescribe();
1533 env->ExceptionClear();
1534 return false;
1535 }
1536
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001537 if (chunk.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001538 return false;
1539 }
1540
1541 /*
1542 * Pull the pieces out of the chunk. We copy the results into a
1543 * newly-allocated buffer that the caller can free. We don't want to
1544 * continue using the Chunk object because nothing has a reference to it.
1545 *
1546 * We could avoid this by returning type/data/offset/length and having
1547 * the caller be aware of the object lifetime issues, but that
1548 * integrates the JDWP code more tightly into the VM, and doesn't work
1549 * if we have responses for multiple chunks.
1550 *
1551 * So we're pretty much stuck with copying data around multiple times.
1552 */
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001553 ScopedLocalRef<jbyteArray> replyData(env, reinterpret_cast<jbyteArray>(env->GetObjectField(chunk.get(), data_fid)));
1554 length = env->GetIntField(chunk.get(), length_fid);
1555 offset = env->GetIntField(chunk.get(), offset_fid);
1556 type = env->GetIntField(chunk.get(), type_fid);
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001557
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001558 LOG(VERBOSE) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
1559 if (length == 0 || replyData.get() == NULL) {
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001560 return false;
1561 }
1562
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001563 jsize replyLength = env->GetArrayLength(replyData.get());
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001564 if (offset + length > replyLength) {
1565 LOG(WARNING) << StringPrintf("chunk off=%d len=%d exceeds reply array len %d", offset, length, replyLength);
1566 return false;
1567 }
1568
1569 uint8_t* reply = new uint8_t[length + kChunkHdrLen];
1570 if (reply == NULL) {
1571 LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
1572 return false;
1573 }
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001574 JDWP::Set4BE(reply + 0, type);
1575 JDWP::Set4BE(reply + 4, length);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001576 env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(reply + kChunkHdrLen));
Elliott Hughesf6a1e1e2011-10-25 16:28:04 -07001577
1578 *pReplyBuf = reply;
1579 *pReplyLen = length + kChunkHdrLen;
1580
1581 LOG(VERBOSE) << StringPrintf("dvmHandleDdm returning type=%.4s buf=%p len=%d", (char*) reply, reply, length);
1582 return true;
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001583}
1584
Elliott Hughesa2155262011-11-16 16:26:58 -08001585void Dbg::DdmBroadcast(bool connect) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001586 LOG(VERBOSE) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
1587
1588 Thread* self = Thread::Current();
1589 if (self->GetState() != Thread::kRunnable) {
1590 LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
1591 /* try anyway? */
1592 }
1593
1594 JNIEnv* env = self->GetJniEnv();
1595 static jclass DdmServer_class = env->FindClass("org/apache/harmony/dalvik/ddmc/DdmServer");
1596 static jmethodID broadcast_mid = env->GetStaticMethodID(DdmServer_class, "broadcast", "(I)V");
1597 jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
1598 env->CallStaticVoidMethod(DdmServer_class, broadcast_mid, event);
1599 if (env->ExceptionCheck()) {
1600 LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
1601 env->ExceptionDescribe();
1602 env->ExceptionClear();
1603 }
1604}
1605
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001606void Dbg::DdmConnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001607 Dbg::DdmBroadcast(true);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001608}
1609
1610void Dbg::DdmDisconnected() {
Elliott Hughesa2155262011-11-16 16:26:58 -08001611 Dbg::DdmBroadcast(false);
Elliott Hughes47fce012011-10-25 18:37:19 -07001612 gDdmThreadNotification = false;
1613}
1614
1615/*
Elliott Hughes82188472011-11-07 18:11:48 -08001616 * Send a notification when a thread starts, stops, or changes its name.
Elliott Hughes47fce012011-10-25 18:37:19 -07001617 *
1618 * Because we broadcast the full set of threads when the notifications are
1619 * first enabled, it's possible for "thread" to be actively executing.
1620 */
Elliott Hughes82188472011-11-07 18:11:48 -08001621void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001622 if (!gDdmThreadNotification) {
1623 return;
1624 }
1625
Elliott Hughes82188472011-11-07 18:11:48 -08001626 if (type == CHUNK_TYPE("THDE")) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001627 uint8_t buf[4];
Elliott Hughesf7c3b662011-10-27 12:04:56 -07001628 JDWP::Set4BE(&buf[0], t->GetThinLockId());
Elliott Hughes47fce012011-10-25 18:37:19 -07001629 Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
Elliott Hughes82188472011-11-07 18:11:48 -08001630 } else {
1631 CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
1632 SirtRef<String> name(t->GetName());
1633 size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
1634 const jchar* chars = name->GetCharArray()->GetData();
1635
Elliott Hughes21f32d72011-11-09 17:44:13 -08001636 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001637 JDWP::Append4BE(bytes, t->GetThinLockId());
1638 JDWP::AppendUtf16BE(bytes, chars, char_count);
Elliott Hughes21f32d72011-11-09 17:44:13 -08001639 CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
1640 Dbg::DdmSendChunk(type, bytes);
Elliott Hughes47fce012011-10-25 18:37:19 -07001641 }
1642}
1643
Elliott Hughesa2155262011-11-16 16:26:58 -08001644static void DdmSendThreadStartCallback(Thread* t, void*) {
Elliott Hughes82188472011-11-07 18:11:48 -08001645 Dbg::DdmSendThreadNotification(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001646}
1647
1648void Dbg::DdmSetThreadNotification(bool enable) {
1649 // We lock the thread list to avoid sending duplicate events or missing
1650 // a thread change. We should be okay holding this lock while sending
1651 // the messages out. (We have to hold it while accessing a live thread.)
Elliott Hughesbbd9d832011-11-07 14:40:00 -08001652 ScopedThreadListLock thread_list_lock;
Elliott Hughes47fce012011-10-25 18:37:19 -07001653
1654 gDdmThreadNotification = enable;
1655 if (enable) {
Elliott Hughesbfe487b2011-10-26 15:48:55 -07001656 Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback, NULL);
Elliott Hughes47fce012011-10-25 18:37:19 -07001657 }
1658}
1659
Elliott Hughesa2155262011-11-16 16:26:58 -08001660void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
Elliott Hughes47fce012011-10-25 18:37:19 -07001661 if (gDebuggerActive) {
1662 JDWP::ObjectId id = gRegistry->Add(t->GetPeer());
Elliott Hughes82188472011-11-07 18:11:48 -08001663 gJdwpState->PostThreadChange(id, type == CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001664 }
Elliott Hughes82188472011-11-07 18:11:48 -08001665 Dbg::DdmSendThreadNotification(t, type);
Elliott Hughes47fce012011-10-25 18:37:19 -07001666}
1667
1668void Dbg::PostThreadStart(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001669 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
Elliott Hughes47fce012011-10-25 18:37:19 -07001670}
1671
1672void Dbg::PostThreadDeath(Thread* t) {
Elliott Hughesa2155262011-11-16 16:26:58 -08001673 Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001674}
1675
Elliott Hughes82188472011-11-07 18:11:48 -08001676void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001677 CHECK(buf != NULL);
1678 iovec vec[1];
1679 vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
1680 vec[0].iov_len = byte_count;
1681 Dbg::DdmSendChunkV(type, vec, 1);
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001682}
1683
Elliott Hughes21f32d72011-11-09 17:44:13 -08001684void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
1685 DdmSendChunk(type, bytes.size(), &bytes[0]);
1686}
1687
Elliott Hughescccd84f2011-12-05 16:51:54 -08001688void Dbg::DdmSendChunkV(uint32_t type, const struct iovec* iov, int iov_count) {
Elliott Hughes3bb81562011-10-21 18:52:59 -07001689 if (gJdwpState == NULL) {
1690 LOG(VERBOSE) << "Debugger thread not active, ignoring DDM send: " << type;
1691 } else {
Elliott Hughescccd84f2011-12-05 16:51:54 -08001692 gJdwpState->DdmSendChunkV(type, iov, iov_count);
Elliott Hughes3bb81562011-10-21 18:52:59 -07001693 }
Elliott Hughes872d4ec2011-10-21 17:07:15 -07001694}
1695
Elliott Hughes767a1472011-10-26 18:49:02 -07001696int Dbg::DdmHandleHpifChunk(HpifWhen when) {
1697 if (when == HPIF_WHEN_NOW) {
Elliott Hughes7162ad92011-10-27 14:08:42 -07001698 DdmSendHeapInfo(when);
Elliott Hughes767a1472011-10-26 18:49:02 -07001699 return true;
1700 }
1701
1702 if (when != HPIF_WHEN_NEVER && when != HPIF_WHEN_NEXT_GC && when != HPIF_WHEN_EVERY_GC) {
1703 LOG(ERROR) << "invalid HpifWhen value: " << static_cast<int>(when);
1704 return false;
1705 }
1706
1707 gDdmHpifWhen = when;
1708 return true;
1709}
1710
1711bool Dbg::DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when, Dbg::HpsgWhat what, bool native) {
1712 if (when != HPSG_WHEN_NEVER && when != HPSG_WHEN_EVERY_GC) {
1713 LOG(ERROR) << "invalid HpsgWhen value: " << static_cast<int>(when);
1714 return false;
1715 }
1716
1717 if (what != HPSG_WHAT_MERGED_OBJECTS && what != HPSG_WHAT_DISTINCT_OBJECTS) {
1718 LOG(ERROR) << "invalid HpsgWhat value: " << static_cast<int>(what);
1719 return false;
1720 }
1721
1722 if (native) {
1723 gDdmNhsgWhen = when;
1724 gDdmNhsgWhat = what;
1725 } else {
1726 gDdmHpsgWhen = when;
1727 gDdmHpsgWhat = what;
1728 }
1729 return true;
1730}
1731
Elliott Hughes7162ad92011-10-27 14:08:42 -07001732void Dbg::DdmSendHeapInfo(HpifWhen reason) {
1733 // If there's a one-shot 'when', reset it.
1734 if (reason == gDdmHpifWhen) {
1735 if (gDdmHpifWhen == HPIF_WHEN_NEXT_GC) {
1736 gDdmHpifWhen = HPIF_WHEN_NEVER;
1737 }
1738 }
1739
1740 /*
1741 * Chunk HPIF (client --> server)
1742 *
1743 * Heap Info. General information about the heap,
1744 * suitable for a summary display.
1745 *
1746 * [u4]: number of heaps
1747 *
1748 * For each heap:
1749 * [u4]: heap ID
1750 * [u8]: timestamp in ms since Unix epoch
1751 * [u1]: capture reason (same as 'when' value from server)
1752 * [u4]: max heap size in bytes (-Xmx)
1753 * [u4]: current heap size in bytes
1754 * [u4]: current number of bytes allocated
1755 * [u4]: current number of objects allocated
1756 */
1757 uint8_t heap_count = 1;
Elliott Hughes21f32d72011-11-09 17:44:13 -08001758 std::vector<uint8_t> bytes;
Elliott Hughes545a0642011-11-08 19:10:03 -08001759 JDWP::Append4BE(bytes, heap_count);
1760 JDWP::Append4BE(bytes, 1); // Heap id (bogus; we only have one heap).
1761 JDWP::Append8BE(bytes, MilliTime());
1762 JDWP::Append1BE(bytes, reason);
1763 JDWP::Append4BE(bytes, Heap::GetMaxMemory()); // Max allowed heap size in bytes.
1764 JDWP::Append4BE(bytes, Heap::GetTotalMemory()); // Current heap size in bytes.
1765 JDWP::Append4BE(bytes, Heap::GetBytesAllocated());
1766 JDWP::Append4BE(bytes, Heap::GetObjectsAllocated());
Elliott Hughes21f32d72011-11-09 17:44:13 -08001767 CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
1768 Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
Elliott Hughes767a1472011-10-26 18:49:02 -07001769}
1770
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001771enum HpsgSolidity {
1772 SOLIDITY_FREE = 0,
1773 SOLIDITY_HARD = 1,
1774 SOLIDITY_SOFT = 2,
1775 SOLIDITY_WEAK = 3,
1776 SOLIDITY_PHANTOM = 4,
1777 SOLIDITY_FINALIZABLE = 5,
1778 SOLIDITY_SWEEP = 6,
1779};
1780
1781enum HpsgKind {
1782 KIND_OBJECT = 0,
1783 KIND_CLASS_OBJECT = 1,
1784 KIND_ARRAY_1 = 2,
1785 KIND_ARRAY_2 = 3,
1786 KIND_ARRAY_4 = 4,
1787 KIND_ARRAY_8 = 5,
1788 KIND_UNKNOWN = 6,
1789 KIND_NATIVE = 7,
1790};
1791
1792#define HPSG_PARTIAL (1<<7)
1793#define HPSG_STATE(solidity, kind) ((uint8_t)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
1794
1795struct HeapChunkContext {
1796 std::vector<uint8_t> buf;
1797 uint8_t* p;
1798 uint8_t* pieceLenField;
1799 size_t totalAllocationUnits;
Elliott Hughes82188472011-11-07 18:11:48 -08001800 uint32_t type;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001801 bool merge;
1802 bool needHeader;
1803
1804 // Maximum chunk size. Obtain this from the formula:
1805 // (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
1806 HeapChunkContext(bool merge, bool native)
1807 : buf(16384 - 16),
1808 type(0),
1809 merge(merge) {
1810 Reset();
1811 if (native) {
1812 type = CHUNK_TYPE("NHSG");
1813 } else {
1814 type = merge ? CHUNK_TYPE("HPSG") : CHUNK_TYPE("HPSO");
1815 }
1816 }
1817
1818 ~HeapChunkContext() {
1819 if (p > &buf[0]) {
1820 Flush();
1821 }
1822 }
1823
1824 void EnsureHeader(const void* chunk_ptr) {
1825 if (!needHeader) {
1826 return;
1827 }
1828
1829 // Start a new HPSx chunk.
1830 JDWP::Write4BE(&p, 1); // Heap id (bogus; we only have one heap).
1831 JDWP::Write1BE(&p, 8); // Size of allocation unit, in bytes.
1832
1833 JDWP::Write4BE(&p, reinterpret_cast<uintptr_t>(chunk_ptr)); // virtual address of segment start.
1834 JDWP::Write4BE(&p, 0); // offset of this piece (relative to the virtual address).
1835 // [u4]: length of piece, in allocation units
1836 // We won't know this until we're done, so save the offset and stuff in a dummy value.
1837 pieceLenField = p;
1838 JDWP::Write4BE(&p, 0x55555555);
1839 needHeader = false;
1840 }
1841
1842 void Flush() {
1843 // Patch the "length of piece" field.
1844 CHECK_LE(&buf[0], pieceLenField);
1845 CHECK_LE(pieceLenField, p);
1846 JDWP::Set4BE(pieceLenField, totalAllocationUnits);
1847
1848 Dbg::DdmSendChunk(type, p - &buf[0], &buf[0]);
1849 Reset();
1850 }
1851
Elliott Hughesa2155262011-11-16 16:26:58 -08001852 static void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len, void* arg) {
1853 reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(chunk_ptr, chunk_len, user_ptr, user_len);
1854 }
1855
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001856 private:
Elliott Hughesa2155262011-11-16 16:26:58 -08001857 enum { ALLOCATION_UNIT_SIZE = 8 };
1858
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001859 void Reset() {
1860 p = &buf[0];
1861 totalAllocationUnits = 0;
1862 needHeader = true;
1863 pieceLenField = NULL;
1864 }
1865
Elliott Hughesa2155262011-11-16 16:26:58 -08001866 void HeapChunkCallback(const void* chunk_ptr, size_t chunk_len, const void* user_ptr, size_t user_len) {
1867 CHECK_EQ((chunk_len & (ALLOCATION_UNIT_SIZE-1)), 0U);
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001868
Elliott Hughesa2155262011-11-16 16:26:58 -08001869 /* Make sure there's enough room left in the buffer.
1870 * We need to use two bytes for every fractional 256
1871 * allocation units used by the chunk.
1872 */
1873 {
1874 size_t needed = (((chunk_len/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
1875 size_t bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1876 if (bytesLeft < needed) {
1877 Flush();
1878 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001879
Elliott Hughesa2155262011-11-16 16:26:58 -08001880 bytesLeft = buf.size() - (size_t)(p - &buf[0]);
1881 if (bytesLeft < needed) {
1882 LOG(WARNING) << "chunk is too big to transmit (chunk_len=" << chunk_len << ", " << needed << " bytes)";
1883 return;
1884 }
1885 }
1886
1887 // OLD-TODO: notice when there's a gap and start a new heap, or at least a new range.
1888 EnsureHeader(chunk_ptr);
1889
1890 // Determine the type of this chunk.
1891 // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
1892 // If it's the same, we should combine them.
1893 uint8_t state = ExamineObject(reinterpret_cast<const Object*>(user_ptr), (type == CHUNK_TYPE("NHSG")));
1894
1895 // Write out the chunk description.
1896 chunk_len /= ALLOCATION_UNIT_SIZE; // convert to allocation units
1897 totalAllocationUnits += chunk_len;
1898 while (chunk_len > 256) {
1899 *p++ = state | HPSG_PARTIAL;
1900 *p++ = 255; // length - 1
1901 chunk_len -= 256;
1902 }
1903 *p++ = state;
1904 *p++ = chunk_len - 1;
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001905 }
1906
Elliott Hughesa2155262011-11-16 16:26:58 -08001907 uint8_t ExamineObject(const Object* o, bool is_native_heap) {
1908 if (o == NULL) {
1909 return HPSG_STATE(SOLIDITY_FREE, 0);
1910 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001911
Elliott Hughesa2155262011-11-16 16:26:58 -08001912 // It's an allocated chunk. Figure out what it is.
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001913
Elliott Hughesa2155262011-11-16 16:26:58 -08001914 // If we're looking at the native heap, we'll just return
1915 // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
1916 if (is_native_heap || !Heap::IsLiveObjectLocked(o)) {
1917 return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
1918 }
1919
1920 Class* c = o->GetClass();
1921 if (c == NULL) {
1922 // The object was probably just created but hasn't been initialized yet.
1923 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1924 }
1925
1926 if (!Heap::IsHeapAddress(c)) {
1927 LOG(WARNING) << "invalid class for managed heap object: " << o << " " << c;
1928 return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
1929 }
1930
1931 if (c->IsClassClass()) {
1932 return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
1933 }
1934
1935 if (c->IsArrayClass()) {
1936 if (o->IsObjectArray()) {
1937 return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1938 }
1939 switch (c->GetComponentSize()) {
1940 case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
1941 case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
1942 case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
1943 case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
1944 }
1945 }
1946
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001947 return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
1948 }
1949
Elliott Hughesa2155262011-11-16 16:26:58 -08001950 DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
1951};
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001952
1953void Dbg::DdmSendHeapSegments(bool native) {
1954 Dbg::HpsgWhen when;
1955 Dbg::HpsgWhat what;
1956 if (!native) {
1957 when = gDdmHpsgWhen;
1958 what = gDdmHpsgWhat;
1959 } else {
1960 when = gDdmNhsgWhen;
1961 what = gDdmNhsgWhat;
1962 }
1963 if (when == HPSG_WHEN_NEVER) {
1964 return;
1965 }
1966
1967 // Figure out what kind of chunks we'll be sending.
1968 CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
1969
1970 // First, send a heap start chunk.
1971 uint8_t heap_id[4];
1972 JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
1973 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
1974
1975 // Send a series of heap segment chunks.
Elliott Hughesa2155262011-11-16 16:26:58 -08001976 HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
1977 if (native) {
1978 dlmalloc_walk_heap(HeapChunkContext::HeapChunkCallback, &context);
1979 } else {
1980 Heap::WalkHeap(HeapChunkContext::HeapChunkCallback, &context);
1981 }
Elliott Hughes6a5bd492011-10-28 14:33:57 -07001982
1983 // Finally, send a heap end chunk.
1984 Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
Elliott Hughes767a1472011-10-26 18:49:02 -07001985}
1986
Elliott Hughes545a0642011-11-08 19:10:03 -08001987void Dbg::SetAllocTrackingEnabled(bool enabled) {
1988 MutexLock mu(gAllocTrackerLock);
1989 if (enabled) {
1990 if (recent_allocation_records_ == NULL) {
1991 LOG(INFO) << "Enabling alloc tracker (" << kNumAllocRecords << " entries, "
1992 << kMaxAllocRecordStackDepth << " frames --> "
1993 << (sizeof(AllocRecord) * kNumAllocRecords) << " bytes)";
1994 gAllocRecordHead = gAllocRecordCount = 0;
1995 recent_allocation_records_ = new AllocRecord[kNumAllocRecords];
1996 CHECK(recent_allocation_records_ != NULL);
1997 }
1998 } else {
1999 delete[] recent_allocation_records_;
2000 recent_allocation_records_ = NULL;
2001 }
2002}
2003
2004struct AllocRecordStackVisitor : public Thread::StackVisitor {
2005 AllocRecordStackVisitor(AllocRecord* record) : record(record), depth(0) {
2006 }
2007
2008 virtual void VisitFrame(const Frame& f, uintptr_t pc) {
2009 if (depth >= kMaxAllocRecordStackDepth) {
2010 return;
2011 }
2012 Method* m = f.GetMethod();
2013 if (m == NULL || m->IsCalleeSaveMethod()) {
2014 return;
2015 }
2016 record->stack[depth].method = m;
2017 record->stack[depth].raw_pc = pc;
2018 ++depth;
2019 }
2020
2021 ~AllocRecordStackVisitor() {
2022 // Clear out any unused stack trace elements.
2023 for (; depth < kMaxAllocRecordStackDepth; ++depth) {
2024 record->stack[depth].method = NULL;
2025 record->stack[depth].raw_pc = 0;
2026 }
2027 }
2028
2029 AllocRecord* record;
2030 size_t depth;
2031};
2032
2033void Dbg::RecordAllocation(Class* type, size_t byte_count) {
2034 Thread* self = Thread::Current();
2035 CHECK(self != NULL);
2036
2037 MutexLock mu(gAllocTrackerLock);
2038 if (recent_allocation_records_ == NULL) {
2039 return;
2040 }
2041
2042 // Advance and clip.
2043 if (++gAllocRecordHead == kNumAllocRecords) {
2044 gAllocRecordHead = 0;
2045 }
2046
2047 // Fill in the basics.
2048 AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
2049 record->type = type;
2050 record->byte_count = byte_count;
2051 record->thin_lock_id = self->GetThinLockId();
2052
2053 // Fill in the stack trace.
2054 AllocRecordStackVisitor visitor(record);
2055 self->WalkStack(&visitor);
2056
2057 if (gAllocRecordCount < kNumAllocRecords) {
2058 ++gAllocRecordCount;
2059 }
2060}
2061
2062/*
2063 * Return the index of the head element.
2064 *
2065 * We point at the most-recently-written record, so if allocRecordCount is 1
2066 * we want to use the current element. Take "head+1" and subtract count
2067 * from it.
2068 *
2069 * We need to handle underflow in our circular buffer, so we add
2070 * kNumAllocRecords and then mask it back down.
2071 */
2072inline static int headIndex() {
2073 return (gAllocRecordHead+1 + kNumAllocRecords - gAllocRecordCount) & (kNumAllocRecords-1);
2074}
2075
2076void Dbg::DumpRecentAllocations() {
2077 MutexLock mu(gAllocTrackerLock);
2078 if (recent_allocation_records_ == NULL) {
2079 LOG(INFO) << "Not recording tracked allocations";
2080 return;
2081 }
2082
2083 // "i" is the head of the list. We want to start at the end of the
2084 // list and move forward to the tail.
2085 size_t i = headIndex();
2086 size_t count = gAllocRecordCount;
2087
2088 LOG(INFO) << "Tracked allocations, (head=" << gAllocRecordHead << " count=" << count << ")";
2089 while (count--) {
2090 AllocRecord* record = &recent_allocation_records_[i];
2091
2092 LOG(INFO) << StringPrintf(" T=%-2d %6d ", record->thin_lock_id, record->byte_count)
2093 << PrettyClass(record->type);
2094
2095 for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
2096 const Method* m = record->stack[stack_frame].method;
2097 if (m == NULL) {
2098 break;
2099 }
2100 LOG(INFO) << " " << PrettyMethod(m) << " line " << record->stack[stack_frame].LineNumber();
2101 }
2102
2103 // pause periodically to help logcat catch up
2104 if ((count % 5) == 0) {
2105 usleep(40000);
2106 }
2107
2108 i = (i + 1) & (kNumAllocRecords-1);
2109 }
2110}
2111
2112class StringTable {
2113 public:
2114 StringTable() {
2115 }
2116
2117 void Add(const String* s) {
2118 table_.insert(s);
2119 }
2120
2121 size_t IndexOf(const String* s) {
2122 return std::distance(table_.begin(), table_.find(s));
2123 }
2124
2125 size_t Size() {
2126 return table_.size();
2127 }
2128
2129 void WriteTo(std::vector<uint8_t>& bytes) {
2130 typedef std::set<const String*>::const_iterator It; // TODO: C++0x auto
2131 for (It it = table_.begin(); it != table_.end(); ++it) {
2132 const String* s = *it;
2133 JDWP::AppendUtf16BE(bytes, s->GetCharArray()->GetData(), s->GetLength());
2134 }
2135 }
2136
2137 private:
2138 std::set<const String*> table_;
2139 DISALLOW_COPY_AND_ASSIGN(StringTable);
2140};
2141
2142/*
2143 * The data we send to DDMS contains everything we have recorded.
2144 *
2145 * Message header (all values big-endian):
2146 * (1b) message header len (to allow future expansion); includes itself
2147 * (1b) entry header len
2148 * (1b) stack frame len
2149 * (2b) number of entries
2150 * (4b) offset to string table from start of message
2151 * (2b) number of class name strings
2152 * (2b) number of method name strings
2153 * (2b) number of source file name strings
2154 * For each entry:
2155 * (4b) total allocation size
2156 * (2b) threadId
2157 * (2b) allocated object's class name index
2158 * (1b) stack depth
2159 * For each stack frame:
2160 * (2b) method's class name
2161 * (2b) method name
2162 * (2b) method source file
2163 * (2b) line number, clipped to 32767; -2 if native; -1 if no source
2164 * (xb) class name strings
2165 * (xb) method name strings
2166 * (xb) source file strings
2167 *
2168 * As with other DDM traffic, strings are sent as a 4-byte length
2169 * followed by UTF-16 data.
2170 *
2171 * We send up 16-bit unsigned indexes into string tables. In theory there
2172 * can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
2173 * each table, but in practice there should be far fewer.
2174 *
2175 * The chief reason for using a string table here is to keep the size of
2176 * the DDMS message to a minimum. This is partly to make the protocol
2177 * efficient, but also because we have to form the whole thing up all at
2178 * once in a memory buffer.
2179 *
2180 * We use separate string tables for class names, method names, and source
2181 * files to keep the indexes small. There will generally be no overlap
2182 * between the contents of these tables.
2183 */
2184jbyteArray Dbg::GetRecentAllocations() {
2185 if (false) {
2186 DumpRecentAllocations();
2187 }
2188
2189 MutexLock mu(gAllocTrackerLock);
2190
2191 /*
2192 * Part 1: generate string tables.
2193 */
2194 StringTable class_names;
2195 StringTable method_names;
2196 StringTable filenames;
2197
2198 int count = gAllocRecordCount;
2199 int idx = headIndex();
2200 while (count--) {
2201 AllocRecord* record = &recent_allocation_records_[idx];
2202
2203 class_names.Add(record->type->GetDescriptor());
2204
2205 for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
2206 const Method* m = record->stack[i].method;
2207 if (m != NULL) {
2208 class_names.Add(m->GetDeclaringClass()->GetDescriptor());
2209 method_names.Add(m->GetName());
2210 filenames.Add(m->GetDeclaringClass()->GetSourceFile());
2211 }
2212 }
2213
2214 idx = (idx + 1) & (kNumAllocRecords-1);
2215 }
2216
2217 LOG(INFO) << "allocation records: " << gAllocRecordCount;
2218
2219 /*
2220 * Part 2: allocate a buffer and generate the output.
2221 */
2222 std::vector<uint8_t> bytes;
2223
2224 // (1b) message header len (to allow future expansion); includes itself
2225 // (1b) entry header len
2226 // (1b) stack frame len
2227 const int kMessageHeaderLen = 15;
2228 const int kEntryHeaderLen = 9;
2229 const int kStackFrameLen = 8;
2230 JDWP::Append1BE(bytes, kMessageHeaderLen);
2231 JDWP::Append1BE(bytes, kEntryHeaderLen);
2232 JDWP::Append1BE(bytes, kStackFrameLen);
2233
2234 // (2b) number of entries
2235 // (4b) offset to string table from start of message
2236 // (2b) number of class name strings
2237 // (2b) number of method name strings
2238 // (2b) number of source file name strings
2239 JDWP::Append2BE(bytes, gAllocRecordCount);
2240 size_t string_table_offset = bytes.size();
2241 JDWP::Append4BE(bytes, 0); // We'll patch this later...
2242 JDWP::Append2BE(bytes, class_names.Size());
2243 JDWP::Append2BE(bytes, method_names.Size());
2244 JDWP::Append2BE(bytes, filenames.Size());
2245
2246 count = gAllocRecordCount;
2247 idx = headIndex();
2248 while (count--) {
2249 // For each entry:
2250 // (4b) total allocation size
2251 // (2b) thread id
2252 // (2b) allocated object's class name index
2253 // (1b) stack depth
2254 AllocRecord* record = &recent_allocation_records_[idx];
2255 size_t stack_depth = record->GetDepth();
2256 JDWP::Append4BE(bytes, record->byte_count);
2257 JDWP::Append2BE(bytes, record->thin_lock_id);
2258 JDWP::Append2BE(bytes, class_names.IndexOf(record->type->GetDescriptor()));
2259 JDWP::Append1BE(bytes, stack_depth);
2260
2261 for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
2262 // For each stack frame:
2263 // (2b) method's class name
2264 // (2b) method name
2265 // (2b) method source file
2266 // (2b) line number, clipped to 32767; -2 if native; -1 if no source
2267 const Method* m = record->stack[stack_frame].method;
2268 JDWP::Append2BE(bytes, class_names.IndexOf(m->GetDeclaringClass()->GetDescriptor()));
2269 JDWP::Append2BE(bytes, method_names.IndexOf(m->GetName()));
2270 JDWP::Append2BE(bytes, filenames.IndexOf(m->GetDeclaringClass()->GetSourceFile()));
2271 JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
2272 }
2273
2274 idx = (idx + 1) & (kNumAllocRecords-1);
2275 }
2276
2277 // (xb) class name strings
2278 // (xb) method name strings
2279 // (xb) source file strings
2280 JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
2281 class_names.WriteTo(bytes);
2282 method_names.WriteTo(bytes);
2283 filenames.WriteTo(bytes);
2284
2285 JNIEnv* env = Thread::Current()->GetJniEnv();
2286 jbyteArray result = env->NewByteArray(bytes.size());
2287 if (result != NULL) {
2288 env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
2289 }
2290 return result;
2291}
2292
Elliott Hughes872d4ec2011-10-21 17:07:15 -07002293} // namespace art