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/*
18 * dalvik.system.VMDebug
19 */
20#include "Dalvik.h"
21#include "native/InternalNativePriv.h"
22
23#include <string.h>
24#include <unistd.h>
25#include <errno.h>
26
27
28/*
29 * Extracts the fd from a FileDescriptor object.
30 *
31 * If an error is encountered, or the extracted descriptor is numerically
32 * invalid, this returns -1 with an exception raised.
33 */
34static int getFileDescriptor(Object* obj)
35{
36    assert(obj != NULL);
37    assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0);
38
39    InstField* field = dvmFindInstanceField(obj->clazz, "descriptor", "I");
40    if (field == NULL) {
41        dvmThrowException("Ljava/lang/NoSuchFieldException;",
42            "No FileDescriptor.descriptor field");
43        return -1;
44    }
45
46    int fd = dvmGetFieldInt(obj, field->byteOffset);
47    if (fd < 0) {
48        dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
49            "Invalid file descriptor");
50        return -1;
51    }
52
53    return fd;
54}
55
56/*
57 * Convert an array of char* into a String[].
58 *
59 * Returns NULL on failure, with an exception raised.
60 */
61static ArrayObject* convertStringArray(char** strings, size_t count)
62{
63    Thread* self = dvmThreadSelf();
64
65    /*
66     * Allocate an array to hold the String objects.
67     */
68    ClassObject* stringArrayClass =
69        dvmFindArrayClass("[Ljava/lang/String;", NULL);
70    if (stringArrayClass == NULL) {
71        /* shouldn't happen */
72        LOGE("Unable to find [Ljava/lang/String;\n");
73        dvmAbort();
74    }
75
76    ArrayObject* stringArray =
77        dvmAllocArrayByClass(stringArrayClass, count, ALLOC_DEFAULT);
78    if (stringArray == NULL) {
79        /* probably OOM */
80        LOGD("Failed allocating array of %d strings\n", count);
81        assert(dvmCheckException(self));
82        return NULL;
83    }
84
85    /*
86     * Create the individual String objects and add them to the array.
87     */
88    size_t i;
89    for (i = 0; i < count; i++) {
90        Object *str =
91            (Object *)dvmCreateStringFromCstr(strings[i]);
92        if (str == NULL) {
93            /* probably OOM; drop out now */
94            assert(dvmCheckException(self));
95            dvmReleaseTrackedAlloc((Object*)stringArray, self);
96            return NULL;
97        }
98        dvmSetObjectArrayElement(stringArray, i, str);
99        /* stored in tracked array, okay to release */
100        dvmReleaseTrackedAlloc(str, self);
101    }
102
103    dvmReleaseTrackedAlloc((Object*)stringArray, self);
104    return stringArray;
105}
106
107/*
108 * static String[] getVmFeatureList()
109 *
110 * Return a set of strings describing available VM features (this is chiefly
111 * of interest to DDMS).  Some features may be controlled by compile-time
112 * or command-line flags.
113 */
114static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args,
115    JValue* pResult)
116{
117    static const int MAX_FEATURE_COUNT = 10;
118    char* features[MAX_FEATURE_COUNT];
119    int idx = 0;
120
121    /* VM responds to DDMS method profiling requests */
122    features[idx++] = "method-trace-profiling";
123    features[idx++] = "method-trace-profiling-streaming";
124#ifdef WITH_HPROF
125    /* VM responds to DDMS heap dump requests */
126    features[idx++] = "hprof-heap-dump";
127    features[idx++] = "hprof-heap-dump-streaming";
128#endif
129
130    assert(idx <= MAX_FEATURE_COUNT);
131
132    LOGV("+++ sending up %d features\n", idx);
133    ArrayObject* arrayObj = convertStringArray(features, idx);
134    RETURN_PTR(arrayObj);       /* will be null on OOM */
135}
136
137
138/* These must match the values in dalvik.system.VMDebug.
139 */
140enum {
141    KIND_ALLOCATED_OBJECTS      = 1<<0,
142    KIND_ALLOCATED_BYTES        = 1<<1,
143    KIND_FREED_OBJECTS          = 1<<2,
144    KIND_FREED_BYTES            = 1<<3,
145    KIND_GC_INVOCATIONS         = 1<<4,
146    KIND_CLASS_INIT_COUNT       = 1<<5,
147    KIND_CLASS_INIT_TIME        = 1<<6,
148#if PROFILE_EXTERNAL_ALLOCATIONS
149    KIND_EXT_ALLOCATED_OBJECTS = 1<<12,
150    KIND_EXT_ALLOCATED_BYTES   = 1<<13,
151    KIND_EXT_FREED_OBJECTS     = 1<<14,
152    KIND_EXT_FREED_BYTES       = 1<<15,
153#endif // PROFILE_EXTERNAL_ALLOCATIONS
154
155    KIND_GLOBAL_ALLOCATED_OBJECTS   = KIND_ALLOCATED_OBJECTS,
156    KIND_GLOBAL_ALLOCATED_BYTES     = KIND_ALLOCATED_BYTES,
157    KIND_GLOBAL_FREED_OBJECTS       = KIND_FREED_OBJECTS,
158    KIND_GLOBAL_FREED_BYTES         = KIND_FREED_BYTES,
159    KIND_GLOBAL_GC_INVOCATIONS      = KIND_GC_INVOCATIONS,
160    KIND_GLOBAL_CLASS_INIT_COUNT    = KIND_CLASS_INIT_COUNT,
161    KIND_GLOBAL_CLASS_INIT_TIME     = KIND_CLASS_INIT_TIME,
162#if PROFILE_EXTERNAL_ALLOCATIONS
163    KIND_GLOBAL_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS,
164    KIND_GLOBAL_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES,
165    KIND_GLOBAL_EXT_FREED_OBJECTS   = KIND_EXT_FREED_OBJECTS,
166    KIND_GLOBAL_EXT_FREED_BYTES     = KIND_EXT_FREED_BYTES,
167#endif // PROFILE_EXTERNAL_ALLOCATIONS
168
169    KIND_THREAD_ALLOCATED_OBJECTS   = KIND_ALLOCATED_OBJECTS << 16,
170    KIND_THREAD_ALLOCATED_BYTES     = KIND_ALLOCATED_BYTES << 16,
171    KIND_THREAD_FREED_OBJECTS       = KIND_FREED_OBJECTS << 16,
172    KIND_THREAD_FREED_BYTES         = KIND_FREED_BYTES << 16,
173#if PROFILE_EXTERNAL_ALLOCATIONS
174    KIND_THREAD_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS << 16,
175    KIND_THREAD_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES << 16,
176    KIND_THREAD_EXT_FREED_OBJECTS   = KIND_EXT_FREED_OBJECTS << 16,
177    KIND_THREAD_EXT_FREED_BYTES     = KIND_EXT_FREED_BYTES << 16,
178#endif // PROFILE_EXTERNAL_ALLOCATIONS
179    KIND_THREAD_GC_INVOCATIONS      = KIND_GC_INVOCATIONS << 16,
180
181    // TODO: failedAllocCount, failedAllocSize
182};
183
184#define KIND_ALL_COUNTS 0xffffffff
185
186/*
187 * Zero out the specified fields.
188 */
189static void clearAllocProfStateFields(AllocProfState *allocProf,
190    unsigned int kinds)
191{
192    if (kinds & KIND_ALLOCATED_OBJECTS) {
193        allocProf->allocCount = 0;
194    }
195    if (kinds & KIND_ALLOCATED_BYTES) {
196        allocProf->allocSize = 0;
197    }
198    if (kinds & KIND_FREED_OBJECTS) {
199        allocProf->freeCount = 0;
200    }
201    if (kinds & KIND_FREED_BYTES) {
202        allocProf->freeSize = 0;
203    }
204    if (kinds & KIND_GC_INVOCATIONS) {
205        allocProf->gcCount = 0;
206    }
207    if (kinds & KIND_CLASS_INIT_COUNT) {
208        allocProf->classInitCount = 0;
209    }
210    if (kinds & KIND_CLASS_INIT_TIME) {
211        allocProf->classInitTime = 0;
212    }
213#if PROFILE_EXTERNAL_ALLOCATIONS
214    if (kinds & KIND_EXT_ALLOCATED_OBJECTS) {
215        allocProf->externalAllocCount = 0;
216    }
217    if (kinds & KIND_EXT_ALLOCATED_BYTES) {
218        allocProf->externalAllocSize = 0;
219    }
220    if (kinds & KIND_EXT_FREED_OBJECTS) {
221        allocProf->externalFreeCount = 0;
222    }
223    if (kinds & KIND_EXT_FREED_BYTES) {
224        allocProf->externalFreeSize = 0;
225    }
226#endif // PROFILE_EXTERNAL_ALLOCATIONS
227}
228
229/*
230 * static void startAllocCounting()
231 *
232 * Reset the counters and enable counting.
233 *
234 * TODO: this currently only resets the per-thread counters for the current
235 * thread.  If we actually start using the per-thread counters we'll
236 * probably want to fix this.
237 */
238static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args,
239    JValue* pResult)
240{
241    UNUSED_PARAMETER(args);
242
243    clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS);
244    clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS);
245    dvmStartAllocCounting();
246    RETURN_VOID();
247}
248
249/*
250 * public static void stopAllocCounting()
251 */
252static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args,
253    JValue* pResult)
254{
255    UNUSED_PARAMETER(args);
256
257    dvmStopAllocCounting();
258    RETURN_VOID();
259}
260
261/*
262 * private static int getAllocCount(int kind)
263 */
264static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args,
265    JValue* pResult)
266{
267    AllocProfState *allocProf;
268    unsigned int kind = args[0];
269    if (kind < (1<<16)) {
270        allocProf = &gDvm.allocProf;
271    } else {
272        allocProf = &dvmThreadSelf()->allocProf;
273        kind >>= 16;
274    }
275    switch (kind) {
276    case KIND_ALLOCATED_OBJECTS:
277        pResult->i = allocProf->allocCount;
278        break;
279    case KIND_ALLOCATED_BYTES:
280        pResult->i = allocProf->allocSize;
281        break;
282    case KIND_FREED_OBJECTS:
283        pResult->i = allocProf->freeCount;
284        break;
285    case KIND_FREED_BYTES:
286        pResult->i = allocProf->freeSize;
287        break;
288    case KIND_GC_INVOCATIONS:
289        pResult->i = allocProf->gcCount;
290        break;
291    case KIND_CLASS_INIT_COUNT:
292        pResult->i = allocProf->classInitCount;
293        break;
294    case KIND_CLASS_INIT_TIME:
295        /* convert nsec to usec, reduce to 32 bits */
296        pResult->i = (int) (allocProf->classInitTime / 1000);
297        break;
298#if PROFILE_EXTERNAL_ALLOCATIONS
299    case KIND_EXT_ALLOCATED_OBJECTS:
300        pResult->i = allocProf->externalAllocCount;
301        break;
302    case KIND_EXT_ALLOCATED_BYTES:
303        pResult->i = allocProf->externalAllocSize;
304        break;
305    case KIND_EXT_FREED_OBJECTS:
306        pResult->i = allocProf->externalFreeCount;
307        break;
308    case KIND_EXT_FREED_BYTES:
309        pResult->i = allocProf->externalFreeSize;
310        break;
311#endif // PROFILE_EXTERNAL_ALLOCATIONS
312    default:
313        assert(false);
314        pResult->i = -1;
315    }
316}
317
318/*
319 * public static void resetAllocCount(int kinds)
320 */
321static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args,
322    JValue* pResult)
323{
324    unsigned int kinds = args[0];
325    clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff);
326    clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16);
327    RETURN_VOID();
328}
329
330/*
331 * static void startMethodTracingNative(String traceFileName,
332 *     FileDescriptor fd, int bufferSize, int flags)
333 *
334 * Start method trace profiling.
335 *
336 * If both "traceFileName" and "fd" are null, the result will be sent
337 * directly to DDMS.  (The non-DDMS versions of the calls are expected
338 * to enforce non-NULL filenames.)
339 */
340static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args,
341    JValue* pResult)
342{
343    StringObject* traceFileStr = (StringObject*) args[0];
344    Object* traceFd = (Object*) args[1];
345    int bufferSize = args[2];
346    int flags = args[3];
347
348    if (bufferSize == 0) {
349        // Default to 8MB per the documentation.
350        bufferSize = 8 * 1024 * 1024;
351    }
352
353    if (bufferSize < 1024) {
354        dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL);
355        RETURN_VOID();
356    }
357
358    char* traceFileName = NULL;
359    if (traceFileStr != NULL)
360        traceFileName = dvmCreateCstrFromString(traceFileStr);
361
362    int fd = -1;
363    if (traceFd != NULL) {
364        int origFd = getFileDescriptor(traceFd);
365        if (origFd < 0)
366            RETURN_VOID();
367
368        fd = dup(origFd);
369        if (fd < 0) {
370            dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
371                "dup(%d) failed: %s", origFd, strerror(errno));
372            RETURN_VOID();
373        }
374    }
375
376    dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]",
377        fd, bufferSize, flags, (traceFileName == NULL && fd == -1));
378    free(traceFileName);
379    RETURN_VOID();
380}
381
382/*
383 * static boolean isMethodTracingActive()
384 *
385 * Determine whether method tracing is currently active.
386 */
387static void Dalvik_dalvik_system_VMDebug_isMethodTracingActive(const u4* args,
388    JValue* pResult)
389{
390    UNUSED_PARAMETER(args);
391
392    RETURN_BOOLEAN(dvmIsMethodTraceActive());
393}
394
395/*
396 * static void stopMethodTracing()
397 *
398 * Stop method tracing.
399 */
400static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args,
401    JValue* pResult)
402{
403    UNUSED_PARAMETER(args);
404
405    dvmMethodTraceStop();
406    RETURN_VOID();
407}
408
409/*
410 * static void startEmulatorTracing()
411 *
412 * Start sending method trace info to the emulator.
413 */
414static void Dalvik_dalvik_system_VMDebug_startEmulatorTracing(const u4* args,
415    JValue* pResult)
416{
417    UNUSED_PARAMETER(args);
418
419    dvmEmulatorTraceStart();
420    RETURN_VOID();
421}
422
423/*
424 * static void stopEmulatorTracing()
425 *
426 * Start sending method trace info to the emulator.
427 */
428static void Dalvik_dalvik_system_VMDebug_stopEmulatorTracing(const u4* args,
429    JValue* pResult)
430{
431    UNUSED_PARAMETER(args);
432
433    dvmEmulatorTraceStop();
434    RETURN_VOID();
435}
436
437/*
438 * static int setAllocationLimit(int limit)
439 *
440 * Set the current allocation limit in this thread.  Return the previous
441 * value.
442 */
443static void Dalvik_dalvik_system_VMDebug_setAllocationLimit(const u4* args,
444    JValue* pResult)
445{
446#if defined(WITH_ALLOC_LIMITS)
447    gDvm.checkAllocLimits = true;
448
449    Thread* self = dvmThreadSelf();
450    int newLimit = args[0];
451    int oldLimit = self->allocLimit;
452
453    if (newLimit < -1) {
454        LOGE("WARNING: bad limit request (%d)\n", newLimit);
455        newLimit = -1;
456    }
457    self->allocLimit = newLimit;
458    RETURN_INT(oldLimit);
459#else
460    UNUSED_PARAMETER(args);
461    RETURN_INT(-1);
462#endif
463}
464
465/*
466 * static int setGlobalAllocationLimit(int limit)
467 *
468 * Set the allocation limit for this process.  Returns the previous value.
469 */
470static void Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit(const u4* args,
471    JValue* pResult)
472{
473#if defined(WITH_ALLOC_LIMITS)
474    gDvm.checkAllocLimits = true;
475
476    int newLimit = args[0];
477    int oldLimit = gDvm.allocationLimit;
478
479    if (newLimit < -1 || newLimit > 0) {
480        LOGE("WARNING: bad limit request (%d)\n", newLimit);
481        newLimit = -1;
482    }
483    // TODO: should use an atomic swap here
484    gDvm.allocationLimit = newLimit;
485    RETURN_INT(oldLimit);
486#else
487    UNUSED_PARAMETER(args);
488    RETURN_INT(-1);
489#endif
490}
491
492/*
493 * static boolean isDebuggerConnected()
494 *
495 * Returns "true" if a debugger is attached.
496 */
497static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args,
498    JValue* pResult)
499{
500    UNUSED_PARAMETER(args);
501
502    RETURN_BOOLEAN(dvmDbgIsDebuggerConnected());
503}
504
505/*
506 * static boolean isDebuggingEnabled()
507 *
508 * Returns "true" if debugging is enabled.
509 */
510static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args,
511    JValue* pResult)
512{
513    UNUSED_PARAMETER(args);
514
515    RETURN_BOOLEAN(gDvm.jdwpConfigured);
516}
517
518/*
519 * static long lastDebuggerActivity()
520 *
521 * Returns the time, in msec, since we last had an interaction with the
522 * debugger (send or receive).
523 */
524static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args,
525    JValue* pResult)
526{
527    UNUSED_PARAMETER(args);
528
529    RETURN_LONG(dvmDbgLastDebuggerActivity());
530}
531
532/*
533 * static void startInstructionCounting()
534 */
535static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args,
536    JValue* pResult)
537{
538    dvmStartInstructionCounting();
539    RETURN_VOID();
540}
541
542/*
543 * static void stopInstructionCounting()
544 */
545static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args,
546    JValue* pResult)
547{
548    dvmStopInstructionCounting();
549    RETURN_VOID();
550}
551
552/*
553 * static boolean getInstructionCount(int[] counts)
554 *
555 * Grab a copy of the global instruction count array.
556 *
557 * Since the instruction counts aren't synchronized, we use sched_yield
558 * to improve our chances of finishing without contention.  (Only makes
559 * sense on a uniprocessor.)
560 */
561static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args,
562    JValue* pResult)
563{
564    ArrayObject* countArray = (ArrayObject*) args[0];
565    int* storage;
566
567    storage = (int*) countArray->contents;
568    sched_yield();
569    memcpy(storage, gDvm.executedInstrCounts,
570        kNumDalvikInstructions * sizeof(int));
571    RETURN_VOID();
572}
573
574/*
575 * static boolean resetInstructionCount()
576 *
577 * Reset the instruction count array.
578 */
579static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args,
580    JValue* pResult)
581{
582    sched_yield();
583    memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int));
584    RETURN_VOID();
585}
586
587/*
588 * static void printLoadedClasses(int flags)
589 *
590 * Dump the list of loaded classes.
591 */
592static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args,
593    JValue* pResult)
594{
595    int flags = args[0];
596
597    dvmDumpAllClasses(flags);
598
599    RETURN_VOID();
600}
601
602/*
603 * static int getLoadedClassCount()
604 *
605 * Return the number of loaded classes
606 */
607static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args,
608    JValue* pResult)
609{
610    int count;
611
612    UNUSED_PARAMETER(args);
613
614    count = dvmGetNumLoadedClasses();
615
616    RETURN_INT(count);
617}
618
619/*
620 * Returns the thread-specific CPU-time clock value for the current thread,
621 * or -1 if the feature isn't supported.
622 */
623static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args,
624    JValue* pResult)
625{
626    jlong result;
627
628#ifdef HAVE_POSIX_CLOCKS
629    struct timespec now;
630    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
631    result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec);
632#else
633    result = (jlong) -1;
634#endif
635
636    RETURN_LONG(result);
637}
638
639/*
640 * static void dumpHprofData(String fileName, FileDescriptor fd)
641 *
642 * Cause "hprof" data to be dumped.  We can throw an IOException if an
643 * error occurs during file handling.
644 */
645static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args,
646    JValue* pResult)
647{
648#ifdef WITH_HPROF
649    StringObject* fileNameStr = (StringObject*) args[0];
650    Object* fileDescriptor = (Object*) args[1];
651    char* fileName;
652    int result;
653
654    /*
655     * Only one of these may be NULL.
656     */
657    if (fileNameStr == NULL && fileDescriptor == NULL) {
658        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
659        RETURN_VOID();
660    }
661
662    if (fileNameStr != NULL) {
663        fileName = dvmCreateCstrFromString(fileNameStr);
664        if (fileName == NULL) {
665            /* unexpected -- malloc failure? */
666            dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?");
667            RETURN_VOID();
668        }
669    } else {
670        fileName = strdup("[fd]");
671    }
672
673    int fd = -1;
674    if (fileDescriptor != NULL) {
675        fd = getFileDescriptor(fileDescriptor);
676        if (fd < 0)
677            RETURN_VOID();
678    }
679
680    result = hprofDumpHeap(fileName, fd, false);
681    free(fileName);
682
683    if (result != 0) {
684        /* ideally we'd throw something more specific based on actual failure */
685        dvmThrowException("Ljava/lang/RuntimeException;",
686            "Failure during heap dump -- check log output for details");
687        RETURN_VOID();
688    }
689#else
690    dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
691#endif
692
693    RETURN_VOID();
694}
695
696/*
697 * static void dumpHprofDataDdms()
698 *
699 * Cause "hprof" data to be computed and sent directly to DDMS.
700 */
701static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args,
702    JValue* pResult)
703{
704#ifdef WITH_HPROF
705    int result;
706
707    result = hprofDumpHeap("[DDMS]", -1, true);
708
709    if (result != 0) {
710        /* ideally we'd throw something more specific based on actual failure */
711        dvmThrowException("Ljava/lang/RuntimeException;",
712            "Failure during heap dump -- check log output for details");
713        RETURN_VOID();
714    }
715#else
716    dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
717#endif
718
719    RETURN_VOID();
720}
721
722/*
723 * static boolean cacheRegisterMap(String classAndMethodDescr)
724 *
725 * If the specified class is loaded, and the named method exists, ensure
726 * that the method's register map is ready for use.  If the class/method
727 * cannot be found, nothing happens.
728 *
729 * This can improve the zygote's sharing of compressed register maps.  Do
730 * this after class preloading.
731 *
732 * Returns true if the register map is cached and ready, either as a result
733 * of this call or earlier activity.  Returns false if the class isn't loaded,
734 * if the method couldn't be found, or if the method has no register map.
735 *
736 * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.)
737 */
738static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args,
739    JValue* pResult)
740{
741    StringObject* classAndMethodDescStr = (StringObject*) args[0];
742    ClassObject* clazz;
743    bool result = false;
744
745    if (classAndMethodDescStr == NULL) {
746        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
747        RETURN_VOID();
748    }
749
750    char* classAndMethodDesc = NULL;
751
752    /*
753     * Pick the string apart.  We have a local copy, so just modify it
754     * in place.
755     */
756    classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr);
757
758    char* methodName = strchr(classAndMethodDesc, '.');
759    if (methodName == NULL) {
760        dvmThrowException("Ljava/lang/RuntimeException;",
761            "method name not found in string");
762        RETURN_VOID();
763    }
764    *methodName++ = '\0';
765
766    char* methodDescr = strchr(methodName, ':');
767    if (methodDescr == NULL) {
768        dvmThrowException("Ljava/lang/RuntimeException;",
769            "method descriptor not found in string");
770        RETURN_VOID();
771    }
772    *methodDescr++ = '\0';
773
774    //LOGD("GOT: %s %s %s\n", classAndMethodDesc, methodName, methodDescr);
775
776    /*
777     * Find the class, but only if it's already loaded.
778     */
779    clazz = dvmLookupClass(classAndMethodDesc, NULL, false);
780    if (clazz == NULL) {
781        LOGD("Class %s not found in bootstrap loader\n", classAndMethodDesc);
782        goto bail;
783    }
784
785    Method* method;
786
787    /*
788     * Find the method, which could be virtual or direct, defined directly
789     * or inherited.
790     */
791    if (methodName[0] == '<') {
792        /*
793         * Constructor or class initializer.  Only need to examine the
794         * "direct" list, and don't need to search up the class hierarchy.
795         */
796        method = dvmFindDirectMethodByDescriptor(clazz, methodName,
797                    methodDescr);
798    } else {
799        /*
800         * Try both lists, and scan up the tree.
801         */
802        method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName,
803                    methodDescr);
804        if (method == NULL) {
805            method = dvmFindDirectMethodHierByDescriptor(clazz, methodName,
806                        methodDescr);
807        }
808    }
809
810    if (method != NULL) {
811        /*
812         * Got it.  See if there's a register map here.
813         */
814        const RegisterMap* pMap;
815        pMap = dvmGetExpandedRegisterMap(method);
816        if (pMap == NULL) {
817            LOGV("No map for %s.%s %s\n",
818                classAndMethodDesc, methodName, methodDescr);
819        } else {
820            LOGV("Found map %s.%s %s\n",
821                classAndMethodDesc, methodName, methodDescr);
822            result = true;
823        }
824    } else {
825        LOGV("Unable to find %s.%s %s\n",
826            classAndMethodDesc, methodName, methodDescr);
827    }
828
829bail:
830    free(classAndMethodDesc);
831    RETURN_BOOLEAN(result);
832}
833
834/*
835 * static void dumpReferenceTables()
836 */
837static void Dalvik_dalvik_system_VMDebug_dumpReferenceTables(const u4* args,
838    JValue* pResult)
839{
840    UNUSED_PARAMETER(args);
841    UNUSED_PARAMETER(pResult);
842
843    LOGI("--- reference table dump ---\n");
844    dvmDumpJniReferenceTables();
845    // could dump thread's internalLocalRefTable, probably not useful
846    // ditto for thread's jniMonitorRefTable
847    LOGI("---\n");
848    RETURN_VOID();
849}
850
851/*
852 * static void crash()
853 *
854 * Dump the current thread's interpreted stack and abort the VM.  Useful
855 * for seeing both interpreted and native stack traces.
856 *
857 * (Might want to restrict this to debuggable processes as a security
858 * measure, or check SecurityManager.checkExit().)
859 */
860static void Dalvik_dalvik_system_VMDebug_crash(const u4* args,
861    JValue* pResult)
862{
863    UNUSED_PARAMETER(args);
864    UNUSED_PARAMETER(pResult);
865
866    LOGW("Crashing VM on request\n");
867    dvmDumpThread(dvmThreadSelf(), false);
868    dvmAbort();
869}
870
871/*
872 * static void infopoint(int id)
873 *
874 * Provide a hook for gdb to hang to so that the VM can be stopped when
875 * user-tagged source locations are being executed.
876 */
877static void Dalvik_dalvik_system_VMDebug_infopoint(const u4* args,
878    JValue* pResult)
879{
880    gDvm.nativeDebuggerActive = true;
881
882    LOGD("VMDebug infopoint %d hit", args[0]);
883
884    gDvm.nativeDebuggerActive = false;
885    RETURN_VOID();
886}
887
888static void Dalvik_dalvik_system_VMDebug_countInstancesOfClass(const u4* args,
889    JValue* pResult)
890{
891    ClassObject* clazz = (ClassObject*)args[0];
892    bool countAssignable = args[1];
893    if (clazz == NULL) {
894        RETURN_LONG(0);
895    }
896    if (countAssignable) {
897        size_t count = dvmCountAssignableInstancesOfClass(clazz);
898        RETURN_LONG((long long)count);
899    } else {
900        size_t count = dvmCountInstancesOfClass(clazz);
901        RETURN_LONG((long long)count);
902    }
903}
904
905const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = {
906    { "getVmFeatureList",           "()[Ljava/lang/String;",
907        Dalvik_dalvik_system_VMDebug_getVmFeatureList },
908    { "getAllocCount",              "(I)I",
909        Dalvik_dalvik_system_VMDebug_getAllocCount },
910    { "resetAllocCount",            "(I)V",
911        Dalvik_dalvik_system_VMDebug_resetAllocCount },
912    { "startAllocCounting",         "()V",
913        Dalvik_dalvik_system_VMDebug_startAllocCounting },
914    { "stopAllocCounting",          "()V",
915        Dalvik_dalvik_system_VMDebug_stopAllocCounting },
916    { "startMethodTracingNative",   "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V",
917        Dalvik_dalvik_system_VMDebug_startMethodTracingNative },
918    { "isMethodTracingActive",      "()Z",
919        Dalvik_dalvik_system_VMDebug_isMethodTracingActive },
920    { "stopMethodTracing",          "()V",
921        Dalvik_dalvik_system_VMDebug_stopMethodTracing },
922    { "startEmulatorTracing",       "()V",
923        Dalvik_dalvik_system_VMDebug_startEmulatorTracing },
924    { "stopEmulatorTracing",        "()V",
925        Dalvik_dalvik_system_VMDebug_stopEmulatorTracing },
926    { "setAllocationLimit",         "(I)I",
927        Dalvik_dalvik_system_VMDebug_setAllocationLimit },
928    { "setGlobalAllocationLimit",   "(I)I",
929        Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit },
930    { "startInstructionCounting",   "()V",
931        Dalvik_dalvik_system_VMDebug_startInstructionCounting },
932    { "stopInstructionCounting",    "()V",
933        Dalvik_dalvik_system_VMDebug_stopInstructionCounting },
934    { "resetInstructionCount",      "()V",
935        Dalvik_dalvik_system_VMDebug_resetInstructionCount },
936    { "getInstructionCount",        "([I)V",
937        Dalvik_dalvik_system_VMDebug_getInstructionCount },
938    { "isDebuggerConnected",        "()Z",
939        Dalvik_dalvik_system_VMDebug_isDebuggerConnected },
940    { "isDebuggingEnabled",         "()Z",
941        Dalvik_dalvik_system_VMDebug_isDebuggingEnabled },
942    { "lastDebuggerActivity",       "()J",
943        Dalvik_dalvik_system_VMDebug_lastDebuggerActivity },
944    { "printLoadedClasses",         "(I)V",
945        Dalvik_dalvik_system_VMDebug_printLoadedClasses },
946    { "getLoadedClassCount",        "()I",
947        Dalvik_dalvik_system_VMDebug_getLoadedClassCount },
948    { "threadCpuTimeNanos",         "()J",
949        Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos },
950    { "dumpHprofData",              "(Ljava/lang/String;Ljava/io/FileDescriptor;)V",
951        Dalvik_dalvik_system_VMDebug_dumpHprofData },
952    { "dumpHprofDataDdms",          "()V",
953        Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms },
954    { "cacheRegisterMap",           "(Ljava/lang/String;)Z",
955        Dalvik_dalvik_system_VMDebug_cacheRegisterMap },
956    { "dumpReferenceTables",        "()V",
957        Dalvik_dalvik_system_VMDebug_dumpReferenceTables },
958    { "crash",                      "()V",
959        Dalvik_dalvik_system_VMDebug_crash },
960    { "infopoint",                 "(I)V",
961        Dalvik_dalvik_system_VMDebug_infopoint },
962    { "countInstancesOfClass",     "(Ljava/lang/Class;Z)J",
963        Dalvik_dalvik_system_VMDebug_countInstancesOfClass },
964    { NULL, NULL, NULL },
965};
966