1 /*
2  * Copyright (C) 2023 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 android.os.profiling;
18 
19 import android.annotation.Nullable;
20 import android.os.Bundle;
21 import android.os.ProfilingManager;
22 import android.provider.DeviceConfig;
23 
24 import perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
25 import perfetto.protos.FtraceConfigOuterClass.FtraceConfig;
26 import perfetto.protos.HeapprofdConfigOuterClass.HeapprofdConfig;
27 import perfetto.protos.JavaHprofConfigOuterClass.JavaHprofConfig;
28 import perfetto.protos.PackagesListConfigOuterClass.PackagesListConfig;
29 import perfetto.protos.PerfEventConfigOuterClass.PerfEventConfig;
30 import perfetto.protos.PerfEventsOuterClass.PerfEvents;
31 import perfetto.protos.ProcessStatsConfigOuterClass.ProcessStatsConfig;
32 import perfetto.protos.TraceConfigOuterClass.TraceConfig;
33 
34 public final class Configs {
35 
36     // Time to wait beyond trace timeout to ensure perfetto has time to finish writing output.
37     private static final int FILE_PROCESSING_DELAY_MS = 2000;
38     // Time used to account for any delay in starting up the underlying profiling process. This
39     // value is used to calculate max profiling time.
40     private static final int MAX_PROFILING_TIME_BUFFER_MS = 10 * 1000;
41 
42     private static boolean sSystemTraceConfigsInitialized = false;
43     private static boolean sHeapProfileConfigsInitialized = false;
44     private static boolean sJavaHeapDumpConfigsInitialized = false;
45     private static boolean sStackSamplingConfigsInitialized = false;
46 
47     private static boolean sKillswitchSystemTrace;
48     private static int sSystemTraceDurationMsDefault;
49     private static int sSystemTraceDurationMsMin;
50     private static int sSystemTraceDurationMsMax;
51     private static int sSystemTraceSizeKbDefault;
52     private static int sSystemTraceSizeKbMin;
53     private static int sSystemTraceSizeKbMax;
54 
55     private static boolean sKillswitchHeapProfile;
56     private static boolean sHeapProfileTrackJavaAllocationsDefault;
57     private static int sHeapProfileFlushTimeoutMsDefault;
58     private static int sHeapProfileDurationMsDefault;
59     private static int sHeapProfileDurationMsMin;
60     private static int sHeapProfileDurationMsMax;
61     private static int sHeapProfileSizeKbDefault;
62     private static int sHeapProfileSizeKbMin;
63     private static int sHeapProfileSizeKbMax;
64     private static long sHeapProfileSamplingIntervalBytesDefault;
65     private static long sHeapProfileSamplingIntervalBytesMin;
66     private static long sHeapProfileSamplingIntervalBytesMax;
67 
68     private static boolean sKillswitchJavaHeapDump;
69     private static int sJavaHeapDumpDurationMsDefault;
70     private static int sJavaHeapDumpDataSourceStopTimeoutMsDefault;
71     private static int sJavaHeapDumpSizeKbDefault;
72     private static int sJavaHeapDumpSizeKbMin;
73     private static int sJavaHeapDumpSizeKbMax;
74 
75     private static boolean sKillswitchStackSampling;
76     private static int sStackSamplingFlushTimeoutMsDefault;
77     private static int sStackSamplingDurationMsDefault;
78     private static int sStackSamplingDurationMsMin;
79     private static int sStackSamplingDurationMsMax;
80     private static int sStackSamplingSizeKbDefault;
81     private static int sStackSamplingSizeKbMin;
82     private static int sStackSamplingSizeKbMax;
83     private static int sStackSamplingSamplingFrequencyDefault;
84     private static int sStackSamplingSamplingFrequencyMin;
85     private static int sStackSamplingSamplingFrequencyMax;
86 
87     /** Initialize System Trace related DeviceConfig set values if they have not been yet. */
initializeSystemTraceConfigsIfNecessary()88     private static void initializeSystemTraceConfigsIfNecessary() {
89         if (sSystemTraceConfigsInitialized) {
90             return;
91         }
92 
93         DeviceConfig.Properties properties = DeviceConfigHelper.getAllSystemTraceProperties();
94 
95         sKillswitchSystemTrace = properties.getBoolean(
96                 DeviceConfigHelper.KILLSWITCH_SYSTEM_TRACE, false);
97         sSystemTraceDurationMsDefault = properties.getInt(
98                 DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_DEFAULT, 300000);
99         sSystemTraceDurationMsMin = properties.getInt(
100                 DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_MIN, 1000);
101         sSystemTraceDurationMsMax = properties.getInt(
102                 DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_MAX, 600000);
103         sSystemTraceSizeKbDefault = properties.getInt(
104                 DeviceConfigHelper.SYSTEM_TRACE_SIZE_KB_DEFAULT, 32768);
105         sSystemTraceSizeKbMin = properties.getInt(
106                 DeviceConfigHelper.SYSTEM_TRACE_SIZE_KB_MIN, 4);
107         sSystemTraceSizeKbMax = properties.getInt(
108                 DeviceConfigHelper.SYSTEM_TRACE_SIZE_KB_MAX, 32768);
109 
110         sSystemTraceConfigsInitialized = true;
111     }
112 
113     /** Initialize Java Heap Dump related DeviceConfig set values if they have not been yet. */
initializeJavaHeapDumpConfigsIfNecessary()114     private static void initializeJavaHeapDumpConfigsIfNecessary() {
115         if (sJavaHeapDumpConfigsInitialized) {
116             return;
117         }
118 
119         DeviceConfig.Properties properties = DeviceConfigHelper.getAllJavaHeapDumpProperties();
120 
121         sKillswitchJavaHeapDump = properties.getBoolean(
122                 DeviceConfigHelper.KILLSWITCH_JAVA_HEAP_DUMP, false);
123         sJavaHeapDumpDurationMsDefault = properties.getInt(
124                 DeviceConfigHelper.JAVA_HEAP_DUMP_DURATION_MS_DEFAULT, 1000);
125         sJavaHeapDumpDataSourceStopTimeoutMsDefault = properties.getInt(
126                 DeviceConfigHelper.JAVA_HEAP_DUMP_DATA_SOURCE_STOP_TIMEOUT_MS_DEFAULT, 100000);
127         sJavaHeapDumpSizeKbDefault = properties.getInt(
128                 DeviceConfigHelper.JAVA_HEAP_DUMP_SIZE_KB_DEFAULT, 256000);
129         sJavaHeapDumpSizeKbMin = properties.getInt(
130                 DeviceConfigHelper.JAVA_HEAP_DUMP_SIZE_KB_MIN, 4);
131         sJavaHeapDumpSizeKbMax = properties.getInt(
132                 DeviceConfigHelper.JAVA_HEAP_DUMP_SIZE_KB_MAX, 256000);
133 
134         sJavaHeapDumpConfigsInitialized = true;
135     }
136 
137     /** Initialize Heap Profile related DeviceConfig set values if they have not been yet. */
initializeHeapProfileConfigsIfNecessary()138     private static void initializeHeapProfileConfigsIfNecessary() {
139         if (sHeapProfileConfigsInitialized) {
140             return;
141         }
142 
143         DeviceConfig.Properties properties = DeviceConfigHelper.getAllHeapProfileProperties();
144 
145         sKillswitchHeapProfile = properties.getBoolean(
146                 DeviceConfigHelper.KILLSWITCH_HEAP_PROFILE, false);
147         sHeapProfileTrackJavaAllocationsDefault = properties.getBoolean(
148                 DeviceConfigHelper.HEAP_PROFILE_TRACK_JAVA_ALLOCATIONS_DEFAULT, false);
149         sHeapProfileFlushTimeoutMsDefault = properties.getInt(
150                 DeviceConfigHelper.HEAP_PROFILE_FLUSH_TIMEOUT_MS_DEFAULT, 30000);
151         sHeapProfileDurationMsDefault = properties.getInt(
152                 DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_DEFAULT, 120000);
153         sHeapProfileDurationMsMin = properties.getInt(
154                 DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_MIN, 1000);
155         sHeapProfileDurationMsMax = properties.getInt(
156                 DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_MAX, 300000);
157         sHeapProfileSizeKbDefault = properties.getInt(
158                 DeviceConfigHelper.HEAP_PROFILE_SIZE_KB_DEFAULT, 65536);
159         sHeapProfileSizeKbMin = properties.getInt(
160                 DeviceConfigHelper.HEAP_PROFILE_SIZE_KB_MIN, 4);
161         sHeapProfileSizeKbMax = properties.getInt(
162                 DeviceConfigHelper.HEAP_PROFILE_SIZE_KB_MAX, 65536);
163         sHeapProfileSamplingIntervalBytesDefault = properties.getLong(
164                 DeviceConfigHelper.HEAP_PROFILE_SAMPLING_INTERVAL_BYTES_DEFAULT, 4096L);
165         sHeapProfileSamplingIntervalBytesMin = properties.getLong(
166                 DeviceConfigHelper.HEAP_PROFILE_SAMPLING_INTERVAL_BYTES_MIN, 1L);
167         sHeapProfileSamplingIntervalBytesMax = properties.getLong(
168                 DeviceConfigHelper.HEAP_PROFILE_SAMPLING_INTERVAL_BYTES_MAX, 65536L);
169 
170         sHeapProfileConfigsInitialized = true;
171     }
172 
173     /** Initialize Stack Sampling related DeviceConfig set values if they have not been yet. */
initializeStackSamplingConfigsIfNecessary()174     private static void initializeStackSamplingConfigsIfNecessary() {
175         if (sStackSamplingConfigsInitialized) {
176             return;
177         }
178 
179         DeviceConfig.Properties properties = DeviceConfigHelper.getAllStackSamplingProperties();
180 
181         sKillswitchStackSampling = properties.getBoolean(
182                 DeviceConfigHelper.KILLSWITCH_STACK_SAMPLING, false);
183         sStackSamplingFlushTimeoutMsDefault = properties.getInt(
184                 DeviceConfigHelper.STACK_SAMPLING_FLUSH_TIMEOUT_MS_DEFAULT, 30000);
185         sStackSamplingDurationMsDefault = properties.getInt(
186                 DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_DEFAULT, 60000);
187         sStackSamplingDurationMsMin = properties.getInt(
188                 DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_MIN, 1000);
189         sStackSamplingDurationMsMax = properties.getInt(
190                 DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_MAX, 300000);
191         sStackSamplingSizeKbDefault = properties.getInt(
192                 DeviceConfigHelper.STACK_SAMPLING_SAMPLING_SIZE_KB_DEFAULT, 65536);
193         sStackSamplingSizeKbMin = properties.getInt(
194                 DeviceConfigHelper.STACK_SAMPLING_SAMPLING_SIZE_KB_MIN, 4);
195         sStackSamplingSizeKbMax = properties.getInt(
196                 DeviceConfigHelper.STACK_SAMPLING_SAMPLING_SIZE_KB_MAX, 65536);
197         sStackSamplingSamplingFrequencyDefault = properties.getInt(
198                 DeviceConfigHelper.STACK_SAMPLING_FREQUENCY_DEFAULT, 100);
199         sStackSamplingSamplingFrequencyMin = properties.getInt(
200                 DeviceConfigHelper.STACK_SAMPLING_FREQUENCY_MIN, 1);
201         sStackSamplingSamplingFrequencyMax = properties.getInt(
202                 DeviceConfigHelper.STACK_SAMPLING_FREQUENCY_MAX, 200);
203 
204         sStackSamplingConfigsInitialized = true;
205     }
206 
207     /**
208      * Update DeviceConfig set configuration values if present in the provided properties, leaving
209      * not present values unchanged.
210      *
211      * Will only update values that have already been initialized as initialization is required
212      * before use and the changed values will be available under the normal access path.
213      */
maybeUpdateConfigs(DeviceConfig.Properties properties)214     public static void maybeUpdateConfigs(DeviceConfig.Properties properties) {
215         // TODO(b/330940387): Revisit defaults before release
216 
217         if (sSystemTraceConfigsInitialized) {
218             sKillswitchSystemTrace = properties.getBoolean(
219                     DeviceConfigHelper.KILLSWITCH_SYSTEM_TRACE, sKillswitchSystemTrace);
220             sSystemTraceDurationMsDefault = properties.getInt(
221                     DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_DEFAULT,
222                     sSystemTraceDurationMsDefault);
223             sSystemTraceDurationMsMin = properties.getInt(
224                     DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_MIN, sSystemTraceDurationMsMin);
225             sSystemTraceDurationMsMax = properties.getInt(
226                     DeviceConfigHelper.SYSTEM_TRACE_DURATION_MS_MAX, sSystemTraceDurationMsMax);
227             sSystemTraceSizeKbDefault = properties.getInt(
228                     DeviceConfigHelper.SYSTEM_TRACE_SIZE_KB_DEFAULT, sSystemTraceSizeKbDefault);
229             sSystemTraceSizeKbMin = properties.getInt(
230                     DeviceConfigHelper.SYSTEM_TRACE_SIZE_KB_MIN, sSystemTraceSizeKbMin);
231             sSystemTraceSizeKbMax = properties.getInt(
232                     DeviceConfigHelper.SYSTEM_TRACE_SIZE_KB_MAX, sSystemTraceSizeKbMax);
233         }
234 
235         if (sHeapProfileConfigsInitialized) {
236             sKillswitchHeapProfile = properties.getBoolean(
237                     DeviceConfigHelper.KILLSWITCH_HEAP_PROFILE, sKillswitchHeapProfile);
238             sHeapProfileTrackJavaAllocationsDefault = properties.getBoolean(
239                     DeviceConfigHelper.HEAP_PROFILE_TRACK_JAVA_ALLOCATIONS_DEFAULT,
240                     sHeapProfileTrackJavaAllocationsDefault);
241             sHeapProfileFlushTimeoutMsDefault = properties.getInt(
242                     DeviceConfigHelper.HEAP_PROFILE_FLUSH_TIMEOUT_MS_DEFAULT,
243                     sHeapProfileFlushTimeoutMsDefault);
244             sHeapProfileDurationMsDefault = properties.getInt(
245                     DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_DEFAULT,
246                     sHeapProfileDurationMsDefault);
247             sHeapProfileDurationMsMin = properties.getInt(
248                     DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_MIN, sHeapProfileDurationMsMin);
249             sHeapProfileDurationMsMax = properties.getInt(
250                     DeviceConfigHelper.HEAP_PROFILE_DURATION_MS_MAX, sHeapProfileDurationMsMax);
251             sHeapProfileSizeKbDefault = properties.getInt(
252                     DeviceConfigHelper.HEAP_PROFILE_SIZE_KB_DEFAULT, sHeapProfileSizeKbDefault);
253             sHeapProfileSizeKbMin = properties.getInt(
254                     DeviceConfigHelper.HEAP_PROFILE_SIZE_KB_MIN, sHeapProfileSizeKbMin);
255             sHeapProfileSizeKbMax = properties.getInt(
256                     DeviceConfigHelper.HEAP_PROFILE_SIZE_KB_MAX, sHeapProfileSizeKbMax);
257             sHeapProfileSamplingIntervalBytesDefault = properties.getLong(
258                     DeviceConfigHelper.HEAP_PROFILE_SAMPLING_INTERVAL_BYTES_DEFAULT,
259                     sHeapProfileSamplingIntervalBytesDefault);
260             sHeapProfileSamplingIntervalBytesMin = properties.getLong(
261                     DeviceConfigHelper.HEAP_PROFILE_SAMPLING_INTERVAL_BYTES_MIN,
262                     sHeapProfileSamplingIntervalBytesMin);
263             sHeapProfileSamplingIntervalBytesMax = properties.getLong(
264                     DeviceConfigHelper.HEAP_PROFILE_SAMPLING_INTERVAL_BYTES_MAX,
265                     sHeapProfileSamplingIntervalBytesMax);
266         }
267 
268         if (sJavaHeapDumpConfigsInitialized) {
269             sKillswitchJavaHeapDump = properties.getBoolean(
270                     DeviceConfigHelper.KILLSWITCH_JAVA_HEAP_DUMP, sKillswitchJavaHeapDump);
271             sJavaHeapDumpDurationMsDefault = properties.getInt(
272                     DeviceConfigHelper.JAVA_HEAP_DUMP_DURATION_MS_DEFAULT,
273                     sJavaHeapDumpDurationMsDefault);
274             sJavaHeapDumpDataSourceStopTimeoutMsDefault = properties.getInt(
275                     DeviceConfigHelper.JAVA_HEAP_DUMP_DATA_SOURCE_STOP_TIMEOUT_MS_DEFAULT,
276                     sJavaHeapDumpDataSourceStopTimeoutMsDefault);
277             sJavaHeapDumpSizeKbDefault = properties.getInt(
278                     DeviceConfigHelper.JAVA_HEAP_DUMP_SIZE_KB_DEFAULT, sJavaHeapDumpSizeKbDefault);
279             sJavaHeapDumpSizeKbMin = properties.getInt(
280                     DeviceConfigHelper.JAVA_HEAP_DUMP_SIZE_KB_MIN, sJavaHeapDumpSizeKbMin);
281             sJavaHeapDumpSizeKbMax = properties.getInt(
282                     DeviceConfigHelper.JAVA_HEAP_DUMP_SIZE_KB_MAX, sJavaHeapDumpSizeKbMax);
283         }
284 
285         if (sStackSamplingConfigsInitialized) {
286             sKillswitchStackSampling = properties.getBoolean(
287                     DeviceConfigHelper.KILLSWITCH_STACK_SAMPLING, sKillswitchStackSampling);
288             sStackSamplingFlushTimeoutMsDefault = properties.getInt(
289                     DeviceConfigHelper.STACK_SAMPLING_FLUSH_TIMEOUT_MS_DEFAULT,
290                     sStackSamplingFlushTimeoutMsDefault);
291             sStackSamplingDurationMsDefault = properties.getInt(
292                     DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_DEFAULT,
293                     sStackSamplingDurationMsDefault);
294             sStackSamplingDurationMsMin = properties.getInt(
295                     DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_MIN, sStackSamplingDurationMsMin);
296             sStackSamplingDurationMsMax = properties.getInt(
297                     DeviceConfigHelper.STACK_SAMPLING_DURATION_MS_MAX, sStackSamplingDurationMsMax);
298             sStackSamplingSizeKbDefault = properties.getInt(
299                     DeviceConfigHelper.STACK_SAMPLING_SAMPLING_SIZE_KB_DEFAULT,
300                     sStackSamplingSizeKbDefault);
301             sStackSamplingSizeKbMin = properties.getInt(
302                     DeviceConfigHelper.STACK_SAMPLING_SAMPLING_SIZE_KB_MIN,
303                     sStackSamplingSizeKbMin);
304             sStackSamplingSizeKbMax = properties.getInt(
305                     DeviceConfigHelper.STACK_SAMPLING_SAMPLING_SIZE_KB_MAX,
306                     sStackSamplingSizeKbMax);
307             sStackSamplingSamplingFrequencyDefault = properties.getInt(
308                     DeviceConfigHelper.STACK_SAMPLING_FREQUENCY_DEFAULT,
309                     sStackSamplingSamplingFrequencyDefault);
310             sStackSamplingSamplingFrequencyMin = properties.getInt(
311                     DeviceConfigHelper.STACK_SAMPLING_FREQUENCY_MIN,
312                     sStackSamplingSamplingFrequencyMin);
313             sStackSamplingSamplingFrequencyMax = properties.getInt(
314                     DeviceConfigHelper.STACK_SAMPLING_FREQUENCY_MAX,
315                     sStackSamplingSamplingFrequencyMax);
316         }
317     }
318 
319     /** This method transforms a request into a useable config for perfetto. */
generateConfigForRequest(int profilingType, final @Nullable Bundle params, String packageName)320     public static byte[] generateConfigForRequest(int profilingType, final @Nullable Bundle params,
321             String packageName) throws IllegalArgumentException {
322         // Create a copy to modify. Entries will be removed from the copy as they're accessed to
323         // ensure that no invalid parameters are present.
324         Bundle paramsCopy = params == null ? null : new Bundle(params);
325 
326         switch (profilingType) {
327             // Java heap dump
328             case ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP:
329                 // This should be unnecessary, but make sure configs are initialized just in case.
330                 initializeJavaHeapDumpConfigsIfNecessary();
331 
332                 if (sKillswitchJavaHeapDump) {
333                     throw new IllegalArgumentException("Java heap dump is disabled");
334                 }
335 
336                 int javaHeapDumpSizeKb = roundUpForBufferSize(getAndRemoveWithinBounds(
337                         ProfilingManager.KEY_SIZE_KB,
338                         sJavaHeapDumpSizeKbDefault,
339                         sJavaHeapDumpSizeKbMin,
340                         sJavaHeapDumpSizeKbMax,
341                         paramsCopy));
342 
343                 confirmEmptyOrThrow(paramsCopy);
344 
345                 return generateJavaHeapDumpConfig(packageName, javaHeapDumpSizeKb);
346 
347             // Heap profile
348             case ProfilingManager.PROFILING_TYPE_HEAP_PROFILE:
349                 // This should be unnecessary, but make sure configs are initialized just in case.
350                 initializeHeapProfileConfigsIfNecessary();
351 
352                 if (sKillswitchHeapProfile) {
353                     throw new IllegalArgumentException("Heap profile is disabled");
354                 }
355 
356                 boolean trackJavaAllocations = getAndRemove(
357                         ProfilingManager.KEY_TRACK_JAVA_ALLOCATIONS,
358                         sHeapProfileTrackJavaAllocationsDefault, paramsCopy);
359                 long samplingIntervalBytes = getAndRemoveWithinBounds(
360                         ProfilingManager.KEY_SAMPLING_INTERVAL_BYTES,
361                         sHeapProfileSamplingIntervalBytesDefault,
362                         sHeapProfileSamplingIntervalBytesMin,
363                         sHeapProfileSamplingIntervalBytesMax,
364                         paramsCopy);
365                 int heapProfileDurationMs = getAndRemoveWithinBounds(
366                         ProfilingManager.KEY_DURATION_MS,
367                         sHeapProfileDurationMsDefault,
368                         sHeapProfileDurationMsMin,
369                         sHeapProfileDurationMsMax,
370                         paramsCopy);
371                 int heapProfileSizeKb = roundUpForBufferSize(getAndRemoveWithinBounds(
372                         ProfilingManager.KEY_SIZE_KB,
373                         sHeapProfileSizeKbDefault,
374                         sHeapProfileSizeKbMin,
375                         sHeapProfileSizeKbMax,
376                         paramsCopy));
377 
378                 confirmEmptyOrThrow(paramsCopy);
379 
380                 return generateHeapProfileConfig(packageName, heapProfileSizeKb,
381                         heapProfileDurationMs, samplingIntervalBytes, trackJavaAllocations);
382 
383             // Stack sampling
384             case ProfilingManager.PROFILING_TYPE_STACK_SAMPLING:
385                 // This should be unnecessary, but make sure configs are initialized just in case.
386                 initializeStackSamplingConfigsIfNecessary();
387 
388                 if (sKillswitchStackSampling) {
389                     throw new IllegalArgumentException("Stack sampling is disabled");
390                 }
391 
392                 long frequency = getAndRemoveWithinBounds(ProfilingManager.KEY_FREQUENCY_HZ,
393                         sStackSamplingSamplingFrequencyDefault,
394                         sStackSamplingSamplingFrequencyMin,
395                         sStackSamplingSamplingFrequencyMax,
396                         paramsCopy);
397                 int stackSamplingDurationMs = getAndRemoveWithinBounds(
398                         ProfilingManager.KEY_DURATION_MS,
399                         sStackSamplingDurationMsDefault,
400                         sStackSamplingDurationMsMin,
401                         sStackSamplingDurationMsMax,
402                         paramsCopy);
403                 int stackSamplingSizeKb = roundUpForBufferSize(getAndRemoveWithinBounds(
404                         ProfilingManager.KEY_SIZE_KB,
405                         sStackSamplingSizeKbDefault,
406                         sStackSamplingSizeKbMin,
407                         sStackSamplingSizeKbMax,
408                         paramsCopy));
409 
410                 confirmEmptyOrThrow(paramsCopy);
411 
412                 return generateStackSamplingConfig(packageName, stackSamplingSizeKb,
413                         stackSamplingDurationMs, frequency);
414 
415             // System trace
416             case ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE:
417                 // This should be unnecessary, but make sure configs are initialized just in case.
418                 initializeSystemTraceConfigsIfNecessary();
419 
420                 if (sKillswitchSystemTrace) {
421                     throw new IllegalArgumentException("System trace is disabled");
422                 }
423 
424                 int systemTraceDurationMs = getAndRemoveWithinBounds(
425                         ProfilingManager.KEY_DURATION_MS,
426                         sSystemTraceDurationMsDefault,
427                         sSystemTraceDurationMsMin,
428                         sSystemTraceDurationMsMax,
429                         paramsCopy);
430                 int systemTraceSizeKb = roundUpForBufferSize(getAndRemoveWithinBounds(
431                         ProfilingManager.KEY_SIZE_KB,
432                         sSystemTraceSizeKbDefault,
433                         sSystemTraceSizeKbMin,
434                         sSystemTraceSizeKbMax,
435                         paramsCopy));
436                 TraceConfig.BufferConfig.FillPolicy systemTraceBufferFillPolicy =
437                         getBufferFillPolicy(getAndRemove(ProfilingManager.KEY_BUFFER_FILL_POLICY,
438                                 ProfilingManager.VALUE_BUFFER_FILL_POLICY_RING_BUFFER, paramsCopy));
439 
440                 confirmEmptyOrThrow(paramsCopy);
441 
442                 return generateSystemTraceConfig(packageName, systemTraceSizeKb,
443                         systemTraceDurationMs, systemTraceBufferFillPolicy);
444 
445             // Invalid type
446             default:
447                 throw new IllegalArgumentException("Invalid profiling type");
448         }
449     }
450 
451     /**
452      * This method returns how long in ms to wait initially before checking if profiling is complete
453      * and rescheduling another check or post processing and cleaning up the result in the event
454      * that it's not stopped manually.
455      */
getInitialProfilingTimeMs(int profilingType, @Nullable Bundle params)456     public static int getInitialProfilingTimeMs(int profilingType,
457             @Nullable Bundle params) {
458         int duration;
459         switch (profilingType) {
460             case ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP:
461                 initializeJavaHeapDumpConfigsIfNecessary();
462                 duration = sJavaHeapDumpDurationMsDefault;
463                 break;
464 
465             case ProfilingManager.PROFILING_TYPE_HEAP_PROFILE:
466                 initializeHeapProfileConfigsIfNecessary();
467                 duration = getWithinBounds(ProfilingManager.KEY_DURATION_MS,
468                         sHeapProfileDurationMsDefault, sHeapProfileDurationMsMin,
469                         sHeapProfileDurationMsMax, params);
470                 break;
471 
472             case ProfilingManager.PROFILING_TYPE_STACK_SAMPLING:
473                 initializeStackSamplingConfigsIfNecessary();
474                 duration = getWithinBounds(ProfilingManager.KEY_DURATION_MS,
475                         sStackSamplingDurationMsDefault, sStackSamplingDurationMsMin,
476                         sStackSamplingDurationMsMax, params);
477                 break;
478 
479             case ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE:
480                 initializeSystemTraceConfigsIfNecessary();
481                 duration = getWithinBounds(ProfilingManager.KEY_DURATION_MS,
482                         sSystemTraceDurationMsDefault, sSystemTraceDurationMsMin,
483                         sSystemTraceDurationMsMax, params);
484                 break;
485 
486             default:
487                 throw new IllegalArgumentException("Invalid profiling type");
488         }
489         return duration + FILE_PROCESSING_DELAY_MS;
490     }
491 
492     /**
493      * This method returns the maximum profiling time allowed for the different profiling types.
494      */
getMaxProfilingTimeAllowedMs(int profilingType, @Nullable Bundle params)495     public static int getMaxProfilingTimeAllowedMs(int profilingType, @Nullable Bundle params) {
496         // Get the initial delay
497         int maxAllowedProcessingTime =
498                 getInitialProfilingTimeMs(profilingType, params);
499 
500         // Add the respective flush and data source timeouts for the types that have them.
501         switch (profilingType) {
502             case ProfilingManager.PROFILING_TYPE_HEAP_PROFILE:
503                 maxAllowedProcessingTime += sHeapProfileFlushTimeoutMsDefault;
504                 break;
505 
506             case ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP:
507                 maxAllowedProcessingTime += sJavaHeapDumpDataSourceStopTimeoutMsDefault;
508                 break;
509 
510             case ProfilingManager.PROFILING_TYPE_STACK_SAMPLING:
511                 maxAllowedProcessingTime += sStackSamplingFlushTimeoutMsDefault;
512                 break;
513         }
514         // Add extra buffer time to account for the time it may take to start the underlying
515         // process.
516         return maxAllowedProcessingTime + MAX_PROFILING_TIME_BUFFER_MS;
517     }
518 
getBufferFillPolicy(int bufferFillPolicy)519     private static TraceConfig.BufferConfig.FillPolicy getBufferFillPolicy(int bufferFillPolicy)
520             throws IllegalArgumentException {
521         switch (bufferFillPolicy) {
522             case ProfilingManager.VALUE_BUFFER_FILL_POLICY_DISCARD:
523                 return TraceConfig.BufferConfig.FillPolicy.DISCARD;
524             case ProfilingManager.VALUE_BUFFER_FILL_POLICY_RING_BUFFER:
525                 return TraceConfig.BufferConfig.FillPolicy.RING_BUFFER;
526             default:
527                 throw new IllegalArgumentException("Invalid buffer fill policy.");
528         }
529     }
530 
getWithinBounds(String key, int defaultValue, int minValue, int maxValue, @Nullable Bundle params)531     private static int getWithinBounds(String key, int defaultValue, int minValue,
532             int maxValue, @Nullable Bundle params) {
533         if (params == null) {
534             return defaultValue;
535         }
536         int value = params.getInt(key, defaultValue);
537         if (value < minValue) {
538             return minValue;
539         } else if (value > maxValue) {
540             return maxValue;
541         } else {
542             return value;
543         }
544     }
545 
getAndRemove(String key, boolean defaultValue, @Nullable Bundle bundle)546     private static boolean getAndRemove(String key, boolean defaultValue, @Nullable Bundle bundle) {
547         if (bundle == null) {
548             return defaultValue;
549         }
550         if (bundle.containsKey(key)) {
551             boolean value = bundle.getBoolean(key);
552             bundle.remove(key);
553             return value;
554         }
555         return defaultValue;
556     }
557 
getAndRemove(String key, int defaultValue, @Nullable Bundle bundle)558     private static int getAndRemove(String key, int defaultValue, @Nullable Bundle bundle) {
559         if (bundle == null) {
560             return defaultValue;
561         }
562         if (bundle.containsKey(key)) {
563             int value = bundle.getInt(key);
564             bundle.remove(key);
565             return value;
566         }
567         return defaultValue;
568     }
569 
getAndRemoveWithinBounds(String key, int defaultValue, int minValue, int maxValue, @Nullable Bundle bundle)570     private static int getAndRemoveWithinBounds(String key, int defaultValue, int minValue,
571             int maxValue, @Nullable Bundle bundle) {
572         if (bundle == null) {
573             return defaultValue;
574         }
575         if (bundle.containsKey(key)) {
576             int value = bundle.getInt(key);
577             bundle.remove(key);
578             if (value < minValue) {
579                 value = minValue;
580             } else if (value > maxValue) {
581                 value = maxValue;
582             }
583             return value;
584         }
585         return defaultValue;
586     }
587 
getAndRemoveWithinBounds(String key, long defaultValue, long minValue, long maxValue, @Nullable Bundle bundle)588     private static long getAndRemoveWithinBounds(String key, long defaultValue, long minValue,
589             long maxValue, @Nullable Bundle bundle) {
590         if (bundle == null) {
591             return defaultValue;
592         }
593         if (bundle.containsKey(key)) {
594             long value = bundle.getLong(key);
595             bundle.remove(key);
596             if (value < minValue) {
597                 value = minValue;
598             } else if (value > maxValue) {
599                 value = maxValue;
600             }
601             return value;
602         }
603         return defaultValue;
604     }
605 
606     /** Buffer sizes are preferred to be multiples of 4kb, round up to next lowest 4 multiple. */
roundUpForBufferSize(int bufferSize)607     private static int roundUpForBufferSize(int bufferSize) {
608         return (bufferSize % 4 == 0) ? bufferSize : bufferSize + (4 - (bufferSize % 4));
609     }
610 
confirmEmptyOrThrow(@ullable Bundle bundle)611     private static void confirmEmptyOrThrow(@Nullable Bundle bundle)
612             throws IllegalArgumentException {
613         if (bundle != null && !bundle.isEmpty()) {
614             throw new IllegalArgumentException(
615                     "Bundle contains invalid or unsupported parameters");
616         }
617     }
618 
generateJavaHeapDumpConfig(String packageName, int bufferSizeKb)619     private static byte[] generateJavaHeapDumpConfig(String packageName, int bufferSizeKb) {
620         TraceConfig.Builder builder = TraceConfig.newBuilder();
621 
622         // Add a buffer
623         TraceConfig.BufferConfig buffer = TraceConfig.BufferConfig.newBuilder()
624                 .setSizeKb(bufferSizeKb)
625                 .setFillPolicy(TraceConfig.BufferConfig.FillPolicy.DISCARD)
626                 .build();
627         builder.addBuffers(buffer);
628 
629         // Add data source
630         JavaHprofConfig javaHprofConfig = JavaHprofConfig.newBuilder()
631                 .addProcessCmdline(packageName)
632                 .setDumpSmaps(true)
633                 .build();
634         DataSourceConfig dataSourceConfig = DataSourceConfig.newBuilder()
635                 .setName("android.java_hprof")
636                 .setJavaHprofConfig(javaHprofConfig)
637                 .build();
638         TraceConfig.DataSource dataSource = TraceConfig.DataSource.newBuilder()
639                 .setConfig(dataSourceConfig)
640                 .build();
641         builder.addDataSources(dataSource);
642 
643         // Add duration and timeout
644         builder.setDurationMs(sJavaHeapDumpDurationMsDefault);
645         builder.setDataSourceStopTimeoutMs(sJavaHeapDumpDataSourceStopTimeoutMsDefault);
646 
647         return builder.build().toByteArray();
648     }
649 
generateHeapProfileConfig(String packageName, int bufferSizeKb, int durationMs, long samplingIntervalBytes, boolean trackJavaAllocations)650     private static byte[] generateHeapProfileConfig(String packageName, int bufferSizeKb,
651             int durationMs, long samplingIntervalBytes, boolean trackJavaAllocations) {
652         TraceConfig.Builder builder = TraceConfig.newBuilder();
653 
654         // Add a buffer
655         TraceConfig.BufferConfig buffer = TraceConfig.BufferConfig.newBuilder()
656                 .setSizeKb(bufferSizeKb)
657                 .setFillPolicy(TraceConfig.BufferConfig.FillPolicy.DISCARD)
658                 .build();
659         builder.addBuffers(buffer);
660 
661         // Add data source
662         HeapprofdConfig.Builder heapprofdConfigBuilder = HeapprofdConfig.newBuilder()
663                 .setShmemSizeBytes(8388608) //8MB
664                 .setSamplingIntervalBytes(samplingIntervalBytes)
665                 .addProcessCmdline(packageName);
666         if (trackJavaAllocations) {
667             heapprofdConfigBuilder.addHeaps("com.android.art");
668         }
669         DataSourceConfig dataSourceConfig = DataSourceConfig.newBuilder()
670                 .setName("android.heapprofd")
671                 .setHeapprofdConfig(heapprofdConfigBuilder.build())
672                 .build();
673         TraceConfig.DataSource dataSource = TraceConfig.DataSource.newBuilder()
674                 .setConfig(dataSourceConfig)
675                 .build();
676         builder.addDataSources(dataSource);
677 
678         // Add duration and timeout
679         builder.setDurationMs(durationMs);
680         builder.setFlushTimeoutMs(sHeapProfileFlushTimeoutMsDefault);
681 
682         return builder.build().toByteArray();
683     }
684 
generateStackSamplingConfig(String packageName, int bufferSizeKb, int durationMs, long frequency)685     private static byte[] generateStackSamplingConfig(String packageName, int bufferSizeKb,
686             int durationMs, long frequency) {
687         TraceConfig.Builder builder = TraceConfig.newBuilder();
688 
689         // Add a buffer
690         TraceConfig.BufferConfig buffer = TraceConfig.BufferConfig.newBuilder()
691                 .setSizeKb(bufferSizeKb)
692                 .setFillPolicy(TraceConfig.BufferConfig.FillPolicy.DISCARD)
693                 .build();
694         builder.addBuffers(buffer);
695 
696         // Add data source
697         PerfEvents.Timebase timebase = PerfEvents.Timebase.newBuilder()
698                 .setCounter(PerfEvents.Counter.SW_CPU_CLOCK)
699                 .setFrequency(frequency)
700                 .setTimestampClock(PerfEvents.PerfClock.PERF_CLOCK_MONOTONIC)
701                 .build();
702         PerfEventConfig.Scope scope = PerfEventConfig.Scope.newBuilder()
703                 .addTargetCmdline(packageName)
704                 .build();
705         PerfEventConfig.CallstackSampling callstackSampling = PerfEventConfig.CallstackSampling
706                 .newBuilder()
707                 .setScope(scope)
708                 .build();
709         PerfEventConfig perfEventConfig = PerfEventConfig.newBuilder()
710                 .setTimebase(timebase)
711                 .setCallstackSampling(callstackSampling)
712                 .build();
713         DataSourceConfig dataSourceConfig = DataSourceConfig.newBuilder()
714                 .setName("linux.perf")
715                 .setPerfEventConfig(perfEventConfig)
716                 .build();
717         TraceConfig.DataSource dataSource = TraceConfig.DataSource.newBuilder()
718                 .setConfig(dataSourceConfig)
719                 .build();
720         builder.addDataSources(dataSource);
721 
722         // Add duration and timeout
723         builder.setDurationMs(durationMs);
724         builder.setFlushTimeoutMs(sStackSamplingFlushTimeoutMsDefault);
725 
726         return builder.build().toByteArray();
727     }
728 
generateSystemTraceConfig(String packageName, int bufferSizeKb, int durationMs, TraceConfig.BufferConfig.FillPolicy bufferFillPolicy)729     private static byte[] generateSystemTraceConfig(String packageName, int bufferSizeKb,
730             int durationMs, TraceConfig.BufferConfig.FillPolicy bufferFillPolicy) {
731         TraceConfig.Builder builder = TraceConfig.newBuilder();
732 
733         // Add 2 buffers, discard for data sources dumped at beginning and ring for contiuously
734         // updated data sources.
735         TraceConfig.BufferConfig buffer0 = TraceConfig.BufferConfig.newBuilder()
736                 .setSizeKb(4096)
737                 .setFillPolicy(TraceConfig.BufferConfig.FillPolicy.DISCARD)
738                 .build();
739         builder.addBuffers(buffer0);
740         TraceConfig.BufferConfig buffer1 = TraceConfig.BufferConfig.newBuilder()
741                 .setSizeKb(bufferSizeKb)
742                 .setFillPolicy(bufferFillPolicy)
743                 .build();
744         builder.addBuffers(buffer1);
745 
746         // Add a whole bunch of data sources
747 
748         // Scan and dump all processes to buffer 0 when trace starts
749         ProcessStatsConfig processStatsConfig = ProcessStatsConfig.newBuilder()
750                 .setScanAllProcessesOnStart(true)
751                 .build();
752         DataSourceConfig dataSourceConfigProcessStats = DataSourceConfig.newBuilder()
753                 .setName("linux.process_stats")
754                 .setTargetBuffer(0)
755                 .setProcessStatsConfig(processStatsConfig)
756                 .build();
757         TraceConfig.DataSource dataSourceProcessStats = TraceConfig.DataSource.newBuilder()
758                 .setConfig(dataSourceConfigProcessStats)
759                 .build();
760         builder.addDataSources(dataSourceProcessStats);
761 
762         // Dump details about the requesting package to buffer 0
763         PackagesListConfig packagesListConfig = PackagesListConfig.newBuilder()
764                 .addPackageNameFilter(packageName)
765                 .build();
766         DataSourceConfig dataSourceConfigPackagesList = DataSourceConfig.newBuilder()
767                 .setName("android.packages_list")
768                 .setTargetBuffer(0)
769                 .setPackagesListConfig(packagesListConfig)
770                 .build();
771         TraceConfig.DataSource dataSourcePackagesList = TraceConfig.DataSource.newBuilder()
772                 .setConfig(dataSourceConfigPackagesList)
773                 .build();
774         builder.addDataSources(dataSourcePackagesList);
775 
776         // Dump select ftrace events to buffer 1
777         FtraceConfig.CompactSchedConfig compactSchedConfig = FtraceConfig.CompactSchedConfig
778                 .newBuilder()
779                 .setEnabled(true)
780                 .build();
781         FtraceConfig ftraceConfig = FtraceConfig.newBuilder()
782                 .setThrottleRssStat(true)
783                 .setDisableGenericEvents(true)
784                 .setCompactSched(compactSchedConfig)
785                 // RSS and ION buffer events:
786                 .addFtraceEvents("gpu_mem/gpu_mem_total")
787                 // Scheduling information & process tracking. Useful for:
788                 // - what is happening on each CPU at each moment
789                 // - why a thread was descheduled
790                 // - parent/child relationships between processes and threads
791                 .addFtraceEvents("power/suspend_resume")
792                 .addFtraceEvents("sched/sched_process_free")
793                 .addFtraceEvents("sched/sched_switch")
794                 .addFtraceEvents("task/task_newtask")
795                 .addFtraceEvents("task/task_rename")
796                 // Wakeup info. Allows you to compute how long a task was:
797                 .addFtraceEvents("sched/sched_waking")
798                 .addFtraceEvents("sched/sched_wakeup_new")
799                 // vmscan and mm_compaction events:
800                 .addFtraceEvents("vmscan/mm_vmscan_direct_reclaim_begin")
801                 .addFtraceEvents("vmscan/mm_vmscan_direct_reclaim_end")
802                 // Atrace activity manager:
803                 .addAtraceCategories("am")
804                 // Java and C:
805                 .addAtraceCategories("dalvik")
806                 // Bionic C library:
807                 .addAtraceCategories("bionic")
808                 // Binder kernel driver
809                 .addAtraceCategories("binder_driver")
810                 // View system:
811                 .addAtraceCategories("view")
812                 // Input:
813                 .addAtraceCategories("input")
814                 // Graphics:
815                 .addAtraceCategories("gfx")
816                 // Enable events for requesting app only:
817                 .addAtraceApps(packageName)
818                 .build();
819         DataSourceConfig dataSourceConfigFtrace = DataSourceConfig.newBuilder()
820                 .setName("linux.ftrace")
821                 .setTargetBuffer(1)
822                 .setFtraceConfig(ftraceConfig)
823                 .build();
824         TraceConfig.DataSource dataSourceFtrace = TraceConfig.DataSource.newBuilder()
825                 .setConfig(dataSourceConfigFtrace)
826                 .build();
827         builder.addDataSources(dataSourceFtrace);
828 
829         // Dump surfaceflinger frame timeline to buffer 1
830         DataSourceConfig dataSourceConfigSurfaceFlinger = DataSourceConfig.newBuilder()
831                 .setName("android.surfaceflinger.frametimeline")
832                 .setTargetBuffer(1)
833                 .build();
834         TraceConfig.DataSource dataSourceSurfaceFlinger = TraceConfig.DataSource.newBuilder()
835                 .setConfig(dataSourceConfigSurfaceFlinger)
836                 .build();
837         builder.addDataSources(dataSourceSurfaceFlinger);
838 
839         // Clear incremental state
840         TraceConfig.IncrementalStateConfig incrementalStateConfig = TraceConfig
841                 .IncrementalStateConfig.newBuilder()
842                         .setClearPeriodMs(10000)
843                         .build();
844         builder.setIncrementalStateConfig(incrementalStateConfig);
845 
846         // Add duration
847         builder.setDurationMs(durationMs);
848 
849         return builder.build().toByteArray();
850     }
851 
852 }
853