summaryrefslogtreecommitdiffstats
path: root/libs/hwui/DisplayListLogBuffer.cpp
blob: 45aaccac7abeed8111ee5c68a2a46e78ecef870c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "DisplayListLogBuffer.h"

// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
// that mStart always points at the next command, not just the next item
#define NUM_COMMANDS 50
#define BUFFER_SIZE ((NUM_COMMANDS) + 1)

/**
 * DisplayListLogBuffer is a utility class which logs the most recent display
 * list operations in a circular buffer. The log is process-wide, because we
 * only care about the most recent operations, not the operations on a per-window
 * basis for a given activity. The purpose of the log is to provide more debugging
 * information in a bug report, by telling us not just where a process hung (which
 * generally is just reported as a stack trace at the Java level) or crashed, but
 * also what happened immediately before that hang or crash. This may help track down
 * problems in the native rendering code or driver interaction related to the display
 * list operations that led up to the hang or crash.
 *
 * The log is implemented as a circular buffer for both space and performance
 * reasons - we only care about the last several operations to give us context
 * leading up to the problem, and we don't want to constantly copy data around or do
 * additional mallocs to keep the most recent operations logged. Only numbers are
 * logged to make the operation fast. If and when the log is output, we process this
 * data into meaningful strings.
 *
 * There is an assumption about the format of the command (currently 2 ints: the
 * opcode and the nesting level). If the type of information logged changes (for example,
 * we may want to save a timestamp), then the size of the buffer and the way the
 * information is recorded in writeCommand() should change to suit.
 */

namespace android {

#ifdef USE_OPENGL_RENDERER
using namespace uirenderer;
ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer);
#endif

namespace uirenderer {


DisplayListLogBuffer::DisplayListLogBuffer() {
    mBufferFirst = (OpLog*) malloc(BUFFER_SIZE * sizeof(OpLog));
    mStart = mBufferFirst;
    mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
    mEnd = mStart;
}

DisplayListLogBuffer::~DisplayListLogBuffer() {
    free(mBufferFirst);
}

/**
 * Called from DisplayListRenderer to output the current buffer into the
 * specified FILE. This only happens in a dumpsys/bugreport operation.
 */
void DisplayListLogBuffer::outputCommands(FILE *file)
{
    OpLog* tmpBufferPtr = mStart;
    while (true) {
        if (tmpBufferPtr == mEnd) {
            break;
        }

        fprintf(file, "%*s%s\n", 2 * tmpBufferPtr->level, "", tmpBufferPtr->label);

        OpLog* nextOp = tmpBufferPtr++;
        if (tmpBufferPtr > mBufferLast) {
            tmpBufferPtr = mBufferFirst;
        }
    }
}

/**
 * Store the given level and label in the buffer and increment/wrap the mEnd
 * and mStart values as appropriate. Label should point to static memory.
 */
void DisplayListLogBuffer::writeCommand(int level, const char* label) {
    mEnd->level = level;
    mEnd->label = label;

    if (mEnd == mBufferLast) {
        mEnd = mBufferFirst;
    } else {
        mEnd++;
    }
    if (mEnd == mStart) {
        mStart++;
        if (mStart > mBufferLast) {
            mStart = mBufferFirst;
        }
    }
}

}; // namespace uirenderer
}; // namespace android