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 * Preparation and completion of hprof data generation.  The output is
19 * written into two files and then combined.  This is necessary because
20 * we generate some of the data (strings and classes) while we dump the
21 * heap, and some analysis tools require that the class and string data
22 * appear first.
23 */
24#include "Hprof.h"
25
26#include <string.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <errno.h>
30#include <sys/time.h>
31#include <time.h>
32
33
34#define kHeadSuffix "-hptemp"
35
36hprof_context_t *
37hprofStartup(const char *outputFileName, int fd, bool directToDdms)
38{
39    hprofStartup_String();
40    hprofStartup_Class();
41#if WITH_HPROF_STACK
42    hprofStartup_StackFrame();
43    hprofStartup_Stack();
44#endif
45
46    hprof_context_t *ctx = malloc(sizeof(*ctx));
47    if (ctx == NULL) {
48        LOGE("hprof: can't allocate context.\n");
49        return NULL;
50    }
51
52    /* pass in name or descriptor of the output file */
53    hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
54
55    assert(ctx->memFp != NULL);
56
57    return ctx;
58}
59
60/*
61 * Finish up the hprof dump.  Returns true on success.
62 */
63bool
64hprofShutdown(hprof_context_t *tailCtx)
65{
66    /* flush the "tail" portion of the output */
67    hprofFlushCurrentRecord(tailCtx);
68
69    /*
70     * Create a new context struct for the start of the file.  We
71     * heap-allocate it so we can share the "free" function.
72     */
73    hprof_context_t *headCtx = malloc(sizeof(*headCtx));
74    if (headCtx == NULL) {
75        LOGE("hprof: can't allocate context.\n");
76        hprofFreeContext(tailCtx);
77        return false;
78    }
79    hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
80        tailCtx->directToDdms);
81
82    LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName);
83    hprofDumpStrings(headCtx);
84    hprofDumpClasses(headCtx);
85
86    /* Write a dummy stack trace record so the analysis
87     * tools don't freak out.
88     */
89    hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
90    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
91    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
92    hprofAddU4ToRecord(&headCtx->curRec, 0);    // no frames
93
94#if WITH_HPROF_STACK
95    hprofDumpStackFrames(headCtx);
96    hprofDumpStacks(headCtx);
97#endif
98
99    hprofFlushCurrentRecord(headCtx);
100
101    hprofShutdown_Class();
102    hprofShutdown_String();
103#if WITH_HPROF_STACK
104    hprofShutdown_Stack();
105    hprofShutdown_StackFrame();
106#endif
107
108    /* flush to ensure memstream pointer and size are updated */
109    fflush(headCtx->memFp);
110    fflush(tailCtx->memFp);
111
112    if (tailCtx->directToDdms) {
113        /* send the data off to DDMS */
114        struct iovec iov[2];
115        iov[0].iov_base = headCtx->fileDataPtr;
116        iov[0].iov_len = headCtx->fileDataSize;
117        iov[1].iov_base = tailCtx->fileDataPtr;
118        iov[1].iov_len = tailCtx->fileDataSize;
119        dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
120    } else {
121        /*
122         * Open the output file, and copy the head and tail to it.
123         */
124        assert(headCtx->fd == tailCtx->fd);
125
126        int outFd;
127        if (headCtx->fd >= 0) {
128            outFd = dup(headCtx->fd);
129            if (outFd < 0) {
130                LOGE("dup(%d) failed: %s\n", headCtx->fd, strerror(errno));
131                /* continue to fail-handler below */
132            }
133        } else {
134            outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT, 0644);
135            if (outFd < 0) {
136                LOGE("can't open %s: %s\n", headCtx->fileName, strerror(errno));
137                /* continue to fail-handler below */
138            }
139        }
140        if (outFd < 0) {
141            hprofFreeContext(headCtx);
142            hprofFreeContext(tailCtx);
143            return false;
144        }
145
146        int result;
147        result = sysWriteFully(outFd, headCtx->fileDataPtr,
148            headCtx->fileDataSize, "hprof-head");
149        result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
150            tailCtx->fileDataSize, "hprof-tail");
151        close(outFd);
152        if (result != 0) {
153            hprofFreeContext(headCtx);
154            hprofFreeContext(tailCtx);
155            return false;
156        }
157    }
158
159    /* throw out a log message for the benefit of "runhat" */
160    LOGI("hprof: heap dump completed (%dKB)\n",
161        (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
162
163    hprofFreeContext(headCtx);
164    hprofFreeContext(tailCtx);
165
166    return true;
167}
168
169/*
170 * Free any heap-allocated items in "ctx", and then free "ctx" itself.
171 */
172void
173hprofFreeContext(hprof_context_t *ctx)
174{
175    assert(ctx != NULL);
176
177    /* we don't own ctx->fd, do not close */
178
179    if (ctx->memFp != NULL)
180        fclose(ctx->memFp);
181    free(ctx->curRec.body);
182    free(ctx->fileName);
183    free(ctx->fileDataPtr);
184    free(ctx);
185}
186