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
17import java.io.Serializable;
18import java.io.IOException;
19import java.io.Writer;
20import java.io.BufferedWriter;
21import java.io.OutputStreamWriter;
22import java.io.FileOutputStream;
23import java.io.FileInputStream;
24import java.io.ObjectInputStream;
25import java.io.BufferedInputStream;
26import java.io.ObjectOutputStream;
27import java.io.BufferedOutputStream;
28import java.util.Map;
29import java.util.HashMap;
30import java.util.Set;
31import java.util.TreeSet;
32import java.util.Arrays;
33import java.nio.charset.Charset;
34
35/**
36 * Root of our data model.
37 */
38public class Root implements Serializable {
39
40    private static final long serialVersionUID = 0;
41
42    /** pid -> Proc */
43    final Map<Integer, Proc> processes = new HashMap<Integer, Proc>();
44
45    /** Class name -> LoadedClass */
46    final Map<String, LoadedClass> loadedClasses
47            = new HashMap<String, LoadedClass>();
48
49    MemoryUsage baseline = MemoryUsage.baseline();
50
51    /**
52     * Records class loads and initializations.
53     */
54    void indexClassOperation(Record record) {
55        Proc process = processes.get(record.pid);
56
57        // Ignore dexopt output. It loads applications classes through the
58        // system class loader and messes us up.
59        if (record.processName.equals("dexopt")) {
60            return;
61        }
62
63        String name = record.className;
64        LoadedClass loadedClass = loadedClasses.get(name);
65        Operation o = null;
66
67        switch (record.type) {
68            case START_LOAD:
69            case START_INIT:
70                if (loadedClass == null) {
71                    loadedClass = new LoadedClass(
72                            name, record.classLoader == 0);
73                    if (loadedClass.systemClass) {
74                        // Only measure memory for classes in the boot
75                        // classpath.
76                        loadedClass.measureMemoryUsage();
77                    }
78                    loadedClasses.put(name, loadedClass);
79                }
80                break;
81
82            case END_LOAD:
83            case END_INIT:
84                o = process.endOperation(record.tid, record.className,
85                        loadedClass, record.time);
86                if (o == null) {
87                    return;
88                }
89        }
90
91        switch (record.type) {
92            case START_LOAD:
93                process.startOperation(record.tid, loadedClass, record.time,
94                        Operation.Type.LOAD);
95                break;
96
97            case START_INIT:
98                process.startOperation(record.tid, loadedClass, record.time,
99                        Operation.Type.INIT);
100                break;
101
102            case END_LOAD:
103                loadedClass.loads.add(o);
104                break;
105
106            case END_INIT:
107                loadedClass.initializations.add(o);
108                break;
109        }
110    }
111
112    /**
113     * Indexes information about the process from the given record.
114     */
115    void indexProcess(Record record) {
116        Proc proc = processes.get(record.pid);
117
118        if (proc == null) {
119            // Create a new process object.
120            Proc parent = processes.get(record.ppid);
121            proc = new Proc(parent, record.pid);
122            processes.put(proc.id, proc);
123            if (parent != null) {
124                parent.children.add(proc);
125            }
126        }
127
128        proc.setName(record.processName);
129    }
130
131    /**
132     * Writes this graph to a file.
133     */
134    void toFile(String fileName) throws IOException {
135        FileOutputStream out = new FileOutputStream(fileName);
136        ObjectOutputStream oout = new ObjectOutputStream(
137                new BufferedOutputStream(out));
138
139        System.err.println("Writing object model...");
140
141        oout.writeObject(this);
142
143        oout.close();
144
145        System.err.println("Done!");
146    }
147
148    /**
149     * Reads Root from a file.
150     */
151    static Root fromFile(String fileName)
152            throws IOException, ClassNotFoundException {
153        FileInputStream fin = new FileInputStream(fileName);
154        ObjectInputStream oin = new ObjectInputStream(
155                new BufferedInputStream(fin));
156
157        Root root = (Root) oin.readObject();
158
159        oin.close();
160
161        return root;
162    }
163}
164