summaryrefslogtreecommitdiffstats
path: root/tools/preload/WritePreloadedClassFile.java
blob: b067bc278b1a838ab144a66153f4a7979e3279c0 (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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * Copyright (C) 2008 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.
 */

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Set;
import java.util.TreeSet;

/**
 * Writes /frameworks/base/preloaded-classes. Also updates
 * {@link LoadedClass#preloaded} fields and writes over compiled log file.
 */
public class WritePreloadedClassFile {

    /**
     * Preload any class that take longer to load than MIN_LOAD_TIME_MICROS us.
     */
    static final int MIN_LOAD_TIME_MICROS = 1250;

    /**
     * Preload any class that was loaded by at least MIN_PROCESSES processes.
     */
    static final int MIN_PROCESSES = 10;

    public static void main(String[] args) throws IOException,
            ClassNotFoundException {
        if (args.length != 1) {
            System.err.println("Usage: WritePreloadedClassFile [compiled log]");
            System.exit(-1);
        }
        String rootFile = args[0];
        Root root = Root.fromFile(rootFile);

        // No classes are preloaded to start.
        for (LoadedClass loadedClass : root.loadedClasses.values()) {
            loadedClass.preloaded = false;
        }

        // Open preloaded-classes file for output.
        Writer out = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(Policy.PRELOADED_CLASS_FILE),
                Charset.forName("US-ASCII")));

        out.write("# Classes which are preloaded by"
                + " com.android.internal.os.ZygoteInit.\n");
        out.write("# Automatically generated by frameworks/base/tools/preload/"
            + WritePreloadedClassFile.class.getSimpleName() + ".java.\n");
        out.write("# MIN_LOAD_TIME_MICROS=" + MIN_LOAD_TIME_MICROS + "\n");
        out.write("# MIN_PROCESSES=" + MIN_PROCESSES + "\n");

        /*
         * The set of classes to preload. We preload a class if:
         *
         *  a) it's loaded in the bootclasspath (i.e., is a system class)
         *  b) it takes > MIN_LOAD_TIME_MICROS us to load, and
         *  c) it's loaded by more than one process, or it's loaded by an
         *     application (i.e., not a long running service)
         */
        Set<LoadedClass> toPreload = new TreeSet<LoadedClass>();

        // Preload classes that were loaded by at least 2 processes. Hopefully,
        // the memory associated with these classes will be shared.
        for (LoadedClass loadedClass : root.loadedClasses.values()) {
            Set<String> names = loadedClass.processNames();
            if (!Policy.isPreloadable(loadedClass)) {
                continue;
            }

            if (names.size() >= MIN_PROCESSES ||
                    (loadedClass.medianTimeMicros() > MIN_LOAD_TIME_MICROS && names.size() > 1)) {
                toPreload.add(loadedClass);
            }
        }

        int initialSize = toPreload.size();
        System.out.println(initialSize
                + " classses were loaded by more than one app.");

        // Preload eligable classes from applications (not long-running
        // services).
        for (Proc proc : root.processes.values()) {
            if (proc.fromZygote() && !Policy.isService(proc.name)) {
                for (Operation operation : proc.operations) {
                    LoadedClass loadedClass = operation.loadedClass;
                    if (shouldPreload(loadedClass)) {
                        toPreload.add(loadedClass);
                    }
                }
            }
        }

        System.out.println("Added " + (toPreload.size() - initialSize)
                + " more to speed up applications.");

        System.out.println(toPreload.size()
                + " total classes will be preloaded.");

        // Make classes that were implicitly loaded by the zygote explicit.
        // This adds minimal overhead but avoid confusion about classes not
        // appearing in the list.
        addAllClassesFrom("zygote", root, toPreload);

        for (LoadedClass loadedClass : toPreload) {
            out.write(loadedClass.name + "\n");
        }

        out.close();

        // Update data to reflect LoadedClass.preloaded changes.
        for (LoadedClass loadedClass : toPreload) {
            loadedClass.preloaded = true;
        }
        root.toFile(rootFile);
    }

    private static void addAllClassesFrom(String processName, Root root,
            Set<LoadedClass> toPreload) {
        for (Proc proc : root.processes.values()) {
            if (proc.name.equals(processName)) {
                for (Operation operation : proc.operations) {
                    boolean preloadable
                            = Policy.isPreloadable(operation.loadedClass);
                    if (preloadable) {
                        toPreload.add(operation.loadedClass);
                    }
                }
            }
        }
    }

    /**
     * Returns true if the class should be preloaded.
     */
    private static boolean shouldPreload(LoadedClass clazz) {
        return Policy.isPreloadable(clazz)
                && clazz.medianTimeMicros() > MIN_LOAD_TIME_MICROS;
    }
}