The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1 | /* |
| 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 | * Handle Dalvik Debug Monitor requests and events. |
| 18 | * |
| 19 | * Remember that all DDM traffic is big-endian since it travels over the |
| 20 | * JDWP connection. |
| 21 | */ |
| 22 | #include "Dalvik.h" |
| 23 | |
| 24 | #include <fcntl.h> |
| 25 | #include <errno.h> |
| 26 | |
| 27 | /* |
| 28 | * "buf" contains a full JDWP packet, possibly with multiple chunks. We |
| 29 | * need to process each, accumulate the replies, and ship the whole thing |
| 30 | * back. |
| 31 | * |
| 32 | * Returns "true" if we have a reply. The reply buffer is newly allocated, |
| 33 | * and includes the chunk type/length, followed by the data. |
| 34 | * |
| 35 | * TODO: we currently assume that the request and reply include a single |
| 36 | * chunk. If this becomes inconvenient we will need to adapt. |
| 37 | */ |
| 38 | bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf, |
| 39 | int* pReplyLen) |
| 40 | { |
| 41 | Thread* self = dvmThreadSelf(); |
| 42 | const int kChunkHdrLen = 8; |
| 43 | ArrayObject* dataArray = NULL; |
| 44 | bool result = false; |
| 45 | |
| 46 | assert(dataLen >= 0); |
| 47 | |
| 48 | /* |
| 49 | * Prep DdmServer. We could throw this in gDvm. |
| 50 | */ |
| 51 | ClassObject* ddmServerClass; |
| 52 | Method* dispatch; |
| 53 | |
| 54 | ddmServerClass = |
| 55 | dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL); |
| 56 | if (ddmServerClass == NULL) { |
| 57 | LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n"); |
| 58 | goto bail; |
| 59 | } |
| 60 | dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch", |
| 61 | "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;"); |
| 62 | if (dispatch == NULL) { |
| 63 | LOGW("Unable to find DdmServer.dispatch\n"); |
| 64 | goto bail; |
| 65 | } |
| 66 | |
| 67 | /* |
| 68 | * Prep Chunk. |
| 69 | */ |
| 70 | int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff; |
| 71 | ClassObject* chunkClass; |
| 72 | chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL); |
| 73 | if (chunkClass == NULL) { |
| 74 | LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n"); |
| 75 | goto bail; |
| 76 | } |
| 77 | chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I"); |
| 78 | chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B"); |
| 79 | chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I"); |
| 80 | chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I"); |
| 81 | if (chunkTypeOff < 0 || chunkDataOff < 0 || |
| 82 | chunkOffsetOff < 0 || chunkLengthOff < 0) |
| 83 | { |
| 84 | LOGW("Unable to find all chunk fields\n"); |
| 85 | goto bail; |
| 86 | } |
| 87 | |
| 88 | /* |
| 89 | * The chunk handlers are written in the Java programming language, so |
| 90 | * we need to convert the buffer to a byte array. |
| 91 | */ |
| 92 | dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT); |
| 93 | if (dataArray == NULL) { |
| 94 | LOGW("array alloc failed (%d)\n", dataLen); |
| 95 | dvmClearException(self); |
| 96 | goto bail; |
| 97 | } |
| 98 | memcpy(dataArray->contents, buf, dataLen); |
| 99 | |
| 100 | /* |
| 101 | * Run through and find all chunks. [Currently just find the first.] |
| 102 | */ |
| 103 | unsigned int offset, length, type; |
| 104 | type = get4BE((u1*)dataArray->contents + 0); |
| 105 | length = get4BE((u1*)dataArray->contents + 4); |
| 106 | offset = kChunkHdrLen; |
| 107 | if (offset+length > (unsigned int) dataLen) { |
| 108 | LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen); |
| 109 | goto bail; |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | * Call the handler. |
| 114 | */ |
| 115 | JValue callRes; |
| 116 | dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset, |
| 117 | length); |
| 118 | if (dvmCheckException(self)) { |
| 119 | LOGI("Exception thrown by dispatcher for 0x%08x\n", type); |
| 120 | dvmLogExceptionStackTrace(); |
| 121 | dvmClearException(self); |
| 122 | goto bail; |
| 123 | } |
| 124 | |
| 125 | Object* chunk; |
| 126 | ArrayObject* replyData; |
| 127 | chunk = (Object*) callRes.l; |
| 128 | if (chunk == NULL) |
| 129 | goto bail; |
| 130 | |
| 131 | /* |
| 132 | * Pull the pieces out of the chunk. We copy the results into a |
| 133 | * newly-allocated buffer that the caller can free. We don't want to |
| 134 | * continue using the Chunk object because nothing has a reference to it. |
| 135 | * (If we do an alloc in here, we need to dvmAddTrackedAlloc it.) |
| 136 | * |
| 137 | * We could avoid this by returning type/data/offset/length and having |
| 138 | * the caller be aware of the object lifetime issues, but that |
| 139 | * integrates the JDWP code more tightly into the VM, and doesn't work |
| 140 | * if we have responses for multiple chunks. |
| 141 | * |
| 142 | * So we're pretty much stuck with copying data around multiple times. |
| 143 | */ |
| 144 | type = dvmGetFieldInt(chunk, chunkTypeOff); |
| 145 | replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff); |
| 146 | offset = dvmGetFieldInt(chunk, chunkOffsetOff); |
| 147 | length = dvmGetFieldInt(chunk, chunkLengthOff); |
| 148 | |
| 149 | LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n", |
| 150 | type, replyData, offset, length); |
| 151 | |
| 152 | if (length == 0 || replyData == NULL) |
| 153 | goto bail; |
| 154 | if (offset + length > replyData->length) { |
| 155 | LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n", |
| 156 | offset, length, replyData->length); |
| 157 | goto bail; |
| 158 | } |
| 159 | |
| 160 | u1* reply; |
| 161 | reply = (u1*) malloc(length + kChunkHdrLen); |
| 162 | if (reply == NULL) { |
| 163 | LOGW("malloc %d failed\n", length+kChunkHdrLen); |
| 164 | goto bail; |
| 165 | } |
| 166 | set4BE(reply + 0, type); |
| 167 | set4BE(reply + 4, length); |
| 168 | memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length); |
| 169 | |
| 170 | *pReplyBuf = reply; |
| 171 | *pReplyLen = length + kChunkHdrLen; |
| 172 | result = true; |
| 173 | |
| 174 | LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n", |
| 175 | (char*) reply, reply, length); |
| 176 | |
| 177 | bail: |
| 178 | dvmReleaseTrackedAlloc((Object*) dataArray, NULL); |
| 179 | return result; |
| 180 | } |
| 181 | |
| 182 | /* defined in org.apache.harmony.dalvik.ddmc.DdmServer */ |
| 183 | #define CONNECTED 1 |
| 184 | #define DISCONNECTED 2 |
| 185 | |
| 186 | /* |
| 187 | * Broadcast an event to all handlers. |
| 188 | */ |
| 189 | static void broadcast(int event) |
| 190 | { |
| 191 | ClassObject* ddmServerClass; |
| 192 | Method* bcast; |
| 193 | |
| 194 | ddmServerClass = |
| 195 | dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL); |
| 196 | if (ddmServerClass == NULL) { |
| 197 | LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n"); |
| 198 | goto bail; |
| 199 | } |
| 200 | bcast = dvmFindDirectMethodByDescriptor(ddmServerClass, "broadcast", "(I)V"); |
| 201 | if (bcast == NULL) { |
| 202 | LOGW("Unable to find DdmServer.broadcast\n"); |
| 203 | goto bail; |
| 204 | } |
| 205 | |
| 206 | Thread* self = dvmThreadSelf(); |
| 207 | |
| 208 | if (self->status != THREAD_RUNNING) { |
| 209 | LOGE("ERROR: DDM broadcast with thread status=%d\n", self->status); |
| 210 | /* try anyway? */ |
| 211 | } |
| 212 | |
| 213 | JValue unused; |
| 214 | dvmCallMethod(self, bcast, NULL, &unused, event); |
| 215 | if (dvmCheckException(self)) { |
| 216 | LOGI("Exception thrown by broadcast(%d)\n", event); |
| 217 | dvmLogExceptionStackTrace(); |
| 218 | dvmClearException(self); |
| 219 | goto bail; |
| 220 | } |
| 221 | |
| 222 | bail: |
| 223 | ; |
| 224 | } |
| 225 | |
| 226 | /* |
| 227 | * First DDM packet has arrived over JDWP. Notify the press. |
| 228 | * |
| 229 | * We can do some initialization here too. |
| 230 | */ |
| 231 | void dvmDdmConnected(void) |
| 232 | { |
| 233 | // TODO: any init |
| 234 | |
| 235 | LOGV("Broadcasting DDM connect\n"); |
| 236 | broadcast(CONNECTED); |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | * JDWP connection has dropped. |
| 241 | * |
| 242 | * Do some cleanup. |
| 243 | */ |
| 244 | void dvmDdmDisconnected(void) |
| 245 | { |
| 246 | LOGV("Broadcasting DDM disconnect\n"); |
| 247 | broadcast(DISCONNECTED); |
| 248 | |
| 249 | gDvm.ddmThreadNotification = false; |
| 250 | } |
| 251 | |
| 252 | |
| 253 | /* |
| 254 | * Turn thread notification on or off. |
| 255 | */ |
| 256 | void dvmDdmSetThreadNotification(bool enable) |
| 257 | { |
| 258 | /* |
| 259 | * We lock the thread list to avoid sending duplicate events or missing |
| 260 | * a thread change. We should be okay holding this lock while sending |
| 261 | * the messages out. (We have to hold it while accessing a live thread.) |
| 262 | */ |
| 263 | dvmLockThreadList(NULL); |
| 264 | gDvm.ddmThreadNotification = enable; |
| 265 | |
| 266 | if (enable) { |
| 267 | Thread* thread; |
| 268 | for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { |
| 269 | //LOGW("notify %d\n", thread->threadId); |
| 270 | dvmDdmSendThreadNotification(thread, true); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | dvmUnlockThreadList(); |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 | * Send a notification when a thread starts or stops. |
| 279 | * |
| 280 | * Because we broadcast the full set of threads when the notifications are |
| 281 | * first enabled, it's possible for "thread" to be actively executing. |
| 282 | */ |
| 283 | void dvmDdmSendThreadNotification(Thread* thread, bool started) |
| 284 | { |
| 285 | if (!gDvm.ddmThreadNotification) |
| 286 | return; |
| 287 | |
| 288 | StringObject* nameObj = (StringObject*) |
| 289 | dvmGetFieldObject(thread->threadObj, gDvm.offJavaLangThread_name); |
| 290 | |
| 291 | int type, len; |
| 292 | u1 buf[256]; |
| 293 | |
| 294 | if (started) { |
| 295 | const u2* chars; |
| 296 | u2* outChars; |
| 297 | size_t stringLen; |
| 298 | |
| 299 | type = CHUNK_TYPE("THCR"); |
| 300 | |
| 301 | if (nameObj != NULL) { |
| 302 | stringLen = dvmStringLen(nameObj); |
| 303 | chars = dvmStringChars(nameObj); |
| 304 | } else { |
| 305 | stringLen = 0; |
| 306 | chars = NULL; |
| 307 | } |
| 308 | |
| 309 | /* leave room for the two integer fields */ |
| 310 | if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2) |
| 311 | stringLen = (sizeof(buf) - sizeof(u4)*2) / 2; |
| 312 | len = stringLen*2 + sizeof(u4)*2; |
| 313 | |
| 314 | set4BE(&buf[0x00], thread->threadId); |
| 315 | set4BE(&buf[0x04], stringLen); |
| 316 | |
| 317 | /* copy the UTF-16 string, transforming to big-endian */ |
| 318 | outChars = (u2*) &buf[0x08]; |
| 319 | while (stringLen--) |
| 320 | set2BE((u1*) (outChars++), *chars++); |
| 321 | } else { |
| 322 | type = CHUNK_TYPE("THDE"); |
| 323 | |
| 324 | len = 4; |
| 325 | |
| 326 | set4BE(&buf[0x00], thread->threadId); |
| 327 | } |
| 328 | |
| 329 | dvmDbgDdmSendChunk(type, len, buf); |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 | * Send a notification when a thread's name changes. |
| 334 | */ |
| 335 | void dvmDdmSendThreadNameChange(int threadId, StringObject* newName) |
| 336 | { |
| 337 | if (!gDvm.ddmThreadNotification) |
| 338 | return; |
| 339 | |
| 340 | size_t stringLen = dvmStringLen(newName); |
| 341 | const u2* chars = dvmStringChars(newName); |
| 342 | |
| 343 | /* |
| 344 | * Output format: |
| 345 | * (4b) thread ID |
| 346 | * (4b) stringLen |
| 347 | * (xb) string chars |
| 348 | */ |
| 349 | int bufLen = 4 + 4 + (stringLen * 2); |
| 350 | u1 buf[bufLen]; |
| 351 | |
| 352 | set4BE(&buf[0x00], threadId); |
| 353 | set4BE(&buf[0x04], stringLen); |
| 354 | u2* outChars = (u2*) &buf[0x08]; |
| 355 | while (stringLen--) |
| 356 | set2BE((u1*) (outChars++), *chars++); |
| 357 | |
| 358 | dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf); |
| 359 | } |
| 360 | |
| 361 | /* |
| 362 | * Get some per-thread stats. |
| 363 | * |
| 364 | * This is currently generated by opening the appropriate "stat" file |
| 365 | * in /proc and reading the pile of stuff that comes out. |
| 366 | */ |
| 367 | static bool getThreadStats(pid_t pid, pid_t tid, unsigned long* pUtime, |
| 368 | unsigned long* pStime) |
| 369 | { |
| 370 | /* |
| 371 | int pid; |
| 372 | char comm[128]; |
| 373 | char state; |
| 374 | int ppid, pgrp, session, tty_nr, tpgid; |
| 375 | unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime; |
| 376 | long cutime, cstime, priority, nice, zero, itrealvalue; |
| 377 | unsigned long starttime, vsize; |
| 378 | long rss; |
| 379 | unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip; |
| 380 | unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap; |
| 381 | int exit_signal, processor; |
| 382 | unsigned long rt_priority, policy; |
| 383 | |
| 384 | scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld " |
| 385 | "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " |
| 386 | "%lu %lu %lu %d %d %lu %lu", |
| 387 | &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid, |
| 388 | &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, |
| 389 | &cutime, &cstime, &priority, &nice, &zero, &itrealvalue, |
| 390 | &starttime, &vsize, &rss, &rlim, &startcode, &endcode, |
| 391 | &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore, |
| 392 | &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor, |
| 393 | &rt_priority, &policy); |
| 394 | */ |
| 395 | |
| 396 | char nameBuf[64]; |
| 397 | int i, fd; |
| 398 | |
| 399 | /* |
| 400 | * Open and read the appropriate file. This is expected to work on |
| 401 | * Linux but will fail on other platforms (e.g. Mac sim). |
| 402 | */ |
| 403 | sprintf(nameBuf, "/proc/%d/task/%d/stat", (int) pid, (int) tid); |
| 404 | fd = open(nameBuf, O_RDONLY); |
| 405 | if (fd < 0) { |
| 406 | LOGV("Unable to open '%s': %s\n", nameBuf, strerror(errno)); |
| 407 | return false; |
| 408 | } |
| 409 | |
| 410 | char lineBuf[512]; // > 2x typical |
| 411 | int cc; |
| 412 | cc = read(fd, lineBuf, sizeof(lineBuf)-1); |
| 413 | if (cc <= 0) { |
Elliott Hughes | c5f53e2 | 2010-06-11 16:13:15 -0700 | [diff] [blame] | 414 | const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno); |
| 415 | LOGI("Unable to read '%s': %s\n", nameBuf, msg); |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 416 | close(fd); |
| 417 | return false; |
| 418 | } |
| 419 | lineBuf[cc] = '\0'; |
| 420 | |
| 421 | /* |
| 422 | * Skip whitespace-separated tokens. |
| 423 | */ |
| 424 | static const char* kWhitespace = " "; |
| 425 | char* cp = lineBuf; |
| 426 | for (i = 0; i < 13; i++) { |
| 427 | cp += strcspn(cp, kWhitespace); // skip token |
| 428 | cp += strspn(cp, kWhitespace); // skip whitespace |
| 429 | } |
| 430 | |
| 431 | /* |
| 432 | * Grab the values we want. |
| 433 | */ |
| 434 | char* endp; |
| 435 | *pUtime = strtoul(cp, &endp, 10); |
| 436 | if (endp == cp) |
| 437 | LOGI("Warning: strtoul failed on utime ('%.30s...')\n", cp); |
| 438 | |
| 439 | cp += strcspn(cp, kWhitespace); |
| 440 | cp += strspn(cp, kWhitespace); |
| 441 | |
| 442 | *pStime = strtoul(cp, &endp, 10); |
| 443 | if (endp == cp) |
| 444 | LOGI("Warning: strtoul failed on stime ('%.30s...')\n", cp); |
| 445 | |
| 446 | close(fd); |
| 447 | return true; |
| 448 | } |
| 449 | |
| 450 | /* |
| 451 | * Generate the contents of a THST chunk. The data encompasses all known |
| 452 | * threads. |
| 453 | * |
| 454 | * Response has: |
| 455 | * (1b) header len |
| 456 | * (1b) bytes per entry |
| 457 | * (2b) thread count |
| 458 | * Then, for each thread: |
| 459 | * (4b) threadId |
| 460 | * (1b) thread status |
| 461 | * (4b) tid |
Carl Shapiro | de75089 | 2010-06-08 16:37:12 -0700 | [diff] [blame] | 462 | * (4b) utime |
| 463 | * (4b) stime |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 464 | * (1b) is daemon? |
| 465 | * |
| 466 | * The length fields exist in anticipation of adding additional fields |
| 467 | * without wanting to break ddms or bump the full protocol version. I don't |
| 468 | * think it warrants full versioning. They might be extraneous and could |
| 469 | * be removed from a future version. |
| 470 | * |
| 471 | * Returns a new byte[] with the data inside, or NULL on failure. The |
| 472 | * caller must call dvmReleaseTrackedAlloc() on the array. |
| 473 | */ |
| 474 | ArrayObject* dvmDdmGenerateThreadStats(void) |
| 475 | { |
| 476 | const int kHeaderLen = 4; |
| 477 | const int kBytesPerEntry = 18; |
| 478 | |
| 479 | dvmLockThreadList(NULL); |
| 480 | |
| 481 | Thread* thread; |
| 482 | int threadCount = 0; |
| 483 | for (thread = gDvm.threadList; thread != NULL; thread = thread->next) |
| 484 | threadCount++; |
| 485 | |
| 486 | /* |
| 487 | * Create a temporary buffer. We can't perform heap allocation with |
| 488 | * the thread list lock held (could cause a GC). The output is small |
| 489 | * enough to sit on the stack. |
| 490 | */ |
| 491 | int bufLen = kHeaderLen + threadCount * kBytesPerEntry; |
| 492 | u1 tmpBuf[bufLen]; |
| 493 | u1* buf = tmpBuf; |
| 494 | |
| 495 | set1(buf+0, kHeaderLen); |
| 496 | set1(buf+1, kBytesPerEntry); |
| 497 | set2BE(buf+2, (u2) threadCount); |
| 498 | buf += kHeaderLen; |
| 499 | |
| 500 | pid_t pid = getpid(); |
| 501 | for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { |
| 502 | unsigned long utime, stime; |
| 503 | bool isDaemon; |
| 504 | |
| 505 | if (!getThreadStats(pid, thread->systemTid, &utime, &stime)) { |
| 506 | // failed; drop in empty values |
| 507 | utime = stime = 0; |
| 508 | } |
| 509 | |
| 510 | isDaemon = dvmGetFieldBoolean(thread->threadObj, |
| 511 | gDvm.offJavaLangThread_daemon); |
| 512 | |
| 513 | set4BE(buf+0, thread->threadId); |
| 514 | set1(buf+4, thread->status); |
| 515 | set4BE(buf+5, thread->systemTid); |
| 516 | set4BE(buf+9, utime); |
| 517 | set4BE(buf+13, stime); |
| 518 | set1(buf+17, isDaemon); |
| 519 | |
| 520 | buf += kBytesPerEntry; |
| 521 | } |
| 522 | dvmUnlockThreadList(); |
| 523 | |
| 524 | |
| 525 | /* |
| 526 | * Create a byte array to hold the data. |
| 527 | */ |
| 528 | ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', bufLen, ALLOC_DEFAULT); |
| 529 | if (arrayObj != NULL) |
| 530 | memcpy(arrayObj->contents, tmpBuf, bufLen); |
| 531 | return arrayObj; |
| 532 | } |
| 533 | |
| 534 | |
| 535 | /* |
| 536 | * Find the specified thread and return its stack trace as an array of |
| 537 | * StackTraceElement objects. |
| 538 | */ |
| 539 | ArrayObject* dvmDdmGetStackTraceById(u4 threadId) |
| 540 | { |
| 541 | Thread* self = dvmThreadSelf(); |
| 542 | Thread* thread; |
| 543 | int* traceBuf; |
| 544 | |
| 545 | dvmLockThreadList(self); |
| 546 | |
| 547 | for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { |
| 548 | if (thread->threadId == threadId) |
| 549 | break; |
| 550 | } |
| 551 | if (thread == NULL) { |
| 552 | LOGI("dvmDdmGetStackTraceById: threadid=%d not found\n", threadId); |
| 553 | dvmUnlockThreadList(); |
| 554 | return NULL; |
| 555 | } |
| 556 | |
| 557 | /* |
| 558 | * Suspend the thread, pull out the stack trace, then resume the thread |
| 559 | * and release the thread list lock. If we're being asked to examine |
| 560 | * our own stack trace, skip the suspend/resume. |
| 561 | */ |
| 562 | int stackDepth = -1; |
| 563 | if (thread != self) |
| 564 | dvmSuspendThread(thread); |
| 565 | traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth); |
| 566 | if (thread != self) |
| 567 | dvmResumeThread(thread); |
| 568 | dvmUnlockThreadList(); |
| 569 | |
| 570 | /* |
| 571 | * Convert the raw buffer into an array of StackTraceElement. |
| 572 | */ |
| 573 | ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth); |
| 574 | free(traceBuf); |
| 575 | return trace; |
| 576 | } |
| 577 | |
| 578 | /* |
| 579 | * Gather up the allocation data and copy it into a byte[]. |
| 580 | * |
| 581 | * Returns NULL on failure with an exception raised. |
| 582 | */ |
| 583 | ArrayObject* dvmDdmGetRecentAllocations(void) |
| 584 | { |
| 585 | u1* data; |
| 586 | size_t len; |
| 587 | |
| 588 | if (!dvmGenerateTrackedAllocationReport(&data, &len)) { |
| 589 | /* assume OOM */ |
| 590 | dvmThrowException("Ljava/lang/OutOfMemoryError;","recent alloc native"); |
| 591 | return NULL; |
| 592 | } |
| 593 | |
| 594 | ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', len, ALLOC_DEFAULT); |
| 595 | if (arrayObj != NULL) |
| 596 | memcpy(arrayObj->contents, data, len); |
| 597 | return arrayObj; |
| 598 | } |