1 /*
2  * Copyright (C) 2012 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 package com.android.gallery3d.util;
18 
19 import android.util.Log;
20 
21 import com.android.gallery3d.common.Utils;
22 
23 import java.io.DataOutputStream;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Map.Entry;
29 
30 // ProfileData keeps profiling samples in a tree structure.
31 // The addSample() method adds a sample. The dumpToFile() method saves the data
32 // to a file. The reset() method clears all samples.
33 public class ProfileData {
34     @SuppressWarnings("unused")
35     private static final String TAG = "ProfileData";
36 
37     private static class Node {
38         public int id;  // this is the name of this node, mapped from mNameToId
39         public Node parent;
40         public int sampleCount;
41         public ArrayList<Node> children;
Node(Node parent, int id)42         public Node(Node parent, int id) {
43             this.parent = parent;
44             this.id = id;
45         }
46     }
47 
48     private Node mRoot;
49     private int mNextId;
50     private HashMap<String, Integer> mNameToId;
51     private DataOutputStream mOut;
52     private byte mScratch[] = new byte[4];  // scratch space for writeInt()
53 
ProfileData()54     public ProfileData() {
55         mRoot = new Node(null, -1);  // The id of the root node is unused.
56         mNameToId = new HashMap<String, Integer>();
57     }
58 
reset()59     public void reset() {
60         mRoot = new Node(null, -1);
61         mNameToId.clear();
62         mNextId = 0;
63     }
64 
nameToId(String name)65     private int nameToId(String name) {
66         Integer id = mNameToId.get(name);
67         if (id == null) {
68             id = ++mNextId;  // The tool doesn't want id=0, so we start from 1.
69             mNameToId.put(name, id);
70         }
71         return id;
72     }
73 
addSample(String[] stack)74     public void addSample(String[] stack) {
75         int[] ids = new int[stack.length];
76         for (int i = 0; i < stack.length; i++) {
77             ids[i] = nameToId(stack[i]);
78         }
79 
80         Node node = mRoot;
81         for (int i = stack.length - 1; i >= 0; i--) {
82             if (node.children == null) {
83                 node.children = new ArrayList<Node>();
84             }
85 
86             int id = ids[i];
87             ArrayList<Node> children = node.children;
88             int j;
89             for (j = 0; j < children.size(); j++) {
90                 if (children.get(j).id == id) break;
91             }
92             if (j == children.size()) {
93                 children.add(new Node(node, id));
94             }
95 
96             node = children.get(j);
97         }
98 
99         node.sampleCount++;
100     }
101 
dumpToFile(String filename)102     public void dumpToFile(String filename) {
103         try {
104             mOut = new DataOutputStream(new FileOutputStream(filename));
105             // Start record
106             writeInt(0);
107             writeInt(3);
108             writeInt(1);
109             writeInt(20000);  // Sampling period: 20ms
110             writeInt(0);
111 
112             // Samples
113             writeAllStacks(mRoot, 0);
114 
115             // End record
116             writeInt(0);
117             writeInt(1);
118             writeInt(0);
119             writeAllSymbols();
120         } catch (IOException ex) {
121             Log.w("Failed to dump to file", ex);
122         } finally {
123             Utils.closeSilently(mOut);
124         }
125     }
126 
127     // Writes out one stack, consisting of N+2 words:
128     // first word: sample count
129     // second word: depth of the stack (N)
130     // N words: each word is the id of one address in the stack
writeOneStack(Node node, int depth)131     private void writeOneStack(Node node, int depth) throws IOException {
132         writeInt(node.sampleCount);
133         writeInt(depth);
134         while (depth-- > 0) {
135             writeInt(node.id);
136             node = node.parent;
137         }
138     }
139 
writeAllStacks(Node node, int depth)140     private void writeAllStacks(Node node, int depth) throws IOException {
141         if (node.sampleCount > 0) {
142             writeOneStack(node, depth);
143         }
144 
145         ArrayList<Node> children = node.children;
146         if (children != null) {
147             for (int i = 0; i < children.size(); i++) {
148                 writeAllStacks(children.get(i), depth + 1);
149             }
150         }
151     }
152 
153     // Writes out the symbol table. Each line is like:
154     // 0x17e java.util.ArrayList.isEmpty(ArrayList.java:319)
writeAllSymbols()155     private void writeAllSymbols() throws IOException {
156         for (Entry<String, Integer> entry : mNameToId.entrySet()) {
157             mOut.writeBytes(String.format("0x%x %s\n", entry.getValue(), entry.getKey()));
158         }
159     }
160 
writeInt(int v)161     private void writeInt(int v) throws IOException {
162         mScratch[0] = (byte) v;
163         mScratch[1] = (byte) (v >> 8);
164         mScratch[2] = (byte) (v >> 16);
165         mScratch[3] = (byte) (v >> 24);
166         mOut.write(mScratch);
167     }
168 }
169