blob: ca42078f28fe4304d81f0d18481ba49e97386589 [file] [log] [blame]
/*
* Copyright (C) 2015 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.
*/
package com.android.builder.profile;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.utils.ILogger;
import com.android.utils.StdLogger;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.UUID;
/**
* Configures and creates instances of {@link ProcessRecorder}.
*
* There can be only one instance of {@link ProcessRecorder} per process (well class loader
* to be exact). This instance can be configured initially before any calls to
* {@link ThreadRecorder#get()} is made. An exception will be thrown if an attempt is made to
* configure the instance of {@link ProcessRecorder} past this initialization window.
*
*/
public class ProcessRecorderFactory {
public static void shutdown() throws InterruptedException {
synchronized (LOCK) {
List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory
.getGarbageCollectorMXBeans();
ThreadRecorder.get().record(ExecutionType.FINAL_METADATA, Recorder.EmptyBlock,
new Recorder.Property("build_time",
Long.toString(System.currentTimeMillis() - sINSTANCE.startTime)),
new Recorder.Property("gc_count",
Long.toString(garbageCollectorMXBeans.get(0).getCollectionCount()
- sINSTANCE.gcCountAtStart)),
new Recorder.Property("gc_time",
Long.toString(garbageCollectorMXBeans.get(0).getCollectionTime()
- sINSTANCE.gcTimeAtStart)));
if (sINSTANCE.isInitialized()) {
sINSTANCE.get().finish();
sINSTANCE.uploadData();
}
sINSTANCE.processRecorder = null;
}
}
public static void initialize(
@NonNull ILogger logger,
@NonNull File out,
@NonNull List<Recorder.Property> properties) throws IOException {
synchronized (LOCK) {
if (sINSTANCE.isInitialized() || !isEnabled()) {
return;
}
sINSTANCE.setLogger(logger);
sINSTANCE.setOutputFile(out);
sINSTANCE.setRecordWriter(new ProcessRecorder.JsonRecordWriter(new FileWriter(out)));
sINSTANCE.get(); // Initialize the ProcessRecorder instance
publishInitialRecords(properties);
}
}
public static void publishInitialRecords(@NonNull List<Recorder.Property> properties) {
List<Recorder.Property> propertyList = Lists.newArrayListWithExpectedSize(
6 + properties.size());
propertyList.add(new Recorder.Property(
"build_id",
UUID.randomUUID().toString()));
propertyList.add(new Recorder.Property(
"os_name",
System.getProperty("os.name")));
propertyList.add(new Recorder.Property(
"os_version",
System.getProperty("os.version")));
propertyList.add(new Recorder.Property(
"java_version",
System.getProperty("java.version")));
propertyList.add(new Recorder.Property(
"java_vm_version",
System.getProperty("java.vm.version")));
propertyList.add(new Recorder.Property(
"max_memory",
Long.toString(Runtime.getRuntime().maxMemory())));
propertyList.addAll(properties);
ThreadRecorder.get().record(
ExecutionType.INITIAL_METADATA,
Recorder.EmptyBlock,
propertyList);
}
private static boolean sENABLED = !Strings.isNullOrEmpty(System.getenv("RECORD_SPANS"));
private final long startTime;
private final long gcCountAtStart;
private final long gcTimeAtStart;
ProcessRecorderFactory() {
startTime = System.currentTimeMillis();
List<GarbageCollectorMXBean> garbageCollectorMXBeans = ManagementFactory
.getGarbageCollectorMXBeans();
gcCountAtStart = garbageCollectorMXBeans.get(0).getCollectionCount();
gcTimeAtStart = garbageCollectorMXBeans.get(0).getCollectionTime();
}
public static void initializeForTests(ProcessRecorder.ExecutionRecordWriter recordWriter) {
sINSTANCE = new ProcessRecorderFactory();
ProcessRecorder.resetForTests();
setEnabled(true);
sINSTANCE.setRecordWriter(recordWriter);
sINSTANCE.get(); // Initialize the ProcessRecorder instance
publishInitialRecords(ImmutableList.<Recorder.Property>of());
}
static boolean isEnabled() {
return sENABLED;
}
@VisibleForTesting
static void setEnabled(boolean enabled) {
sENABLED = enabled;
}
/**
* Sets the {@link ProcessRecorder.JsonRecordWriter }
* @param recordWriter
*/
public synchronized void setRecordWriter(
@NonNull ProcessRecorder.ExecutionRecordWriter recordWriter) {
assertRecorderNotCreated();
this.recordWriter = recordWriter;
}
public synchronized void setLogger(@NonNull ILogger iLogger) {
assertRecorderNotCreated();
this.iLogger = iLogger;
}
public static ProcessRecorderFactory getFactory() {
return sINSTANCE;
}
boolean isInitialized() {
return processRecorder != null;
}
@SuppressWarnings("VariableNotUsedInsideIf")
private void assertRecorderNotCreated() {
if (isInitialized()) {
throw new RuntimeException("ProcessRecorder already created.");
}
}
static final Object LOCK = new Object();
static ProcessRecorderFactory sINSTANCE = new ProcessRecorderFactory();
@Nullable
private ProcessRecorder processRecorder = null;
@Nullable
private ProcessRecorder.ExecutionRecordWriter recordWriter = null;
@Nullable
private ILogger iLogger = null;
private File outputFile = null;
private void setOutputFile(File outputFile) {
this.outputFile = outputFile;
}
synchronized ProcessRecorder get() {
if (processRecorder == null) {
if (recordWriter == null) {
throw new RuntimeException("recordWriter not configured.");
}
if (iLogger == null) {
iLogger = new StdLogger(StdLogger.Level.INFO);
}
processRecorder = new ProcessRecorder(recordWriter, iLogger);
}
return processRecorder;
}
private void uploadData() {
if (outputFile == null) {
return;
}
try {
URL u = new URL("http://android-devtools-logging.appspot.com/log/");
HttpURLConnection conn = null;
conn = (HttpURLConnection) u.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(outputFile.length()));
InputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(outputFile));
OutputStream os = conn.getOutputStream();
ByteStreams.copy(is, os);
os.close();
} finally {
if (is != null) {
is.close();
}
}
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((line = reader.readLine()) != null) {
if (iLogger != null) {
iLogger.info("From POST : " + line);
}
}
reader.close();
} catch(Exception e) {
if (iLogger != null) {
iLogger.warning("An exception while generated while uploading the profiler data");
iLogger.error(e, "Exception while uploading the profiler data");
}
}
}
}