
com.android.builder.profile.ThreadRecorder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of builder Show documentation
Show all versions of builder Show documentation
Library to build Android applications.
/*
* 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.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Facility to record block execution time on a single thread. Threads should not be spawned during
* the block execution as its processing will not be recorded as of the parent's execution time.
*
* // TODO : provide facilities to create a new ThreadRecorder using a parent so the slave threads
* can be connected to the parent's task.
*/
public class ThreadRecorder implements Recorder {
private static final Logger logger = Logger.getLogger(ThreadRecorder.class.getName());
// Dummy implementation that records nothing but comply to the overall recording contracts.
protected static final Recorder dummyRecorder = new Recorder() {
@Nullable
@Override
public T record(@NonNull ExecutionType executionType, @NonNull Block block,
Property... properties) {
return record(executionType, block, Collections.emptyList());
}
@Nullable
@Override
public T record(@NonNull ExecutionType executionType, @NonNull Block block,
@NonNull List properties) {
try {
return block.call();
} catch (Exception e) {
block.handleException(e);
}
return null;
}
@Override
public long allocationRecordId() {
return 0;
}
@Override
public void closeRecord(ExecutionRecord record) {
}
};
private static final Recorder recorder = new ThreadRecorder();
public static Recorder get() {
return ProcessRecorderFactory.getFactory().isInitialized() ? recorder : dummyRecorder;
}
private static class PartialRecord {
final ExecutionType executionType;
final long recordId;
final long parentRecordId;
final long startTimeInMs;
final List extraArgs;
PartialRecord(ExecutionType executionType,
long recordId,
long parentId,
long startTimeInMs,
List extraArgs) {
this.executionType = executionType;
this.recordId = recordId;
this.parentRecordId = parentId;
this.startTimeInMs = startTimeInMs;
this.extraArgs = extraArgs;
}
}
/**
* Do not put anything else than JDK classes in the ThreadLocal as it prevents that class
* and therefore the plugin classloader to be gc'ed leading to OOM or PermGen issues.
*/
protected final ThreadLocal> recordStacks =
new ThreadLocal>() {
@Override
protected Deque initialValue() {
return new ArrayDeque();
}
};
@Override
public long allocationRecordId() {
long recordId = ProcessRecorder.allocateRecordId();
recordStacks.get().push(recordId);
return recordId;
}
@Override
public void closeRecord(ExecutionRecord executionRecord) {
if (recordStacks.get().pop() != executionRecord.id) {
logger.severe("Internal Error : mixed records in profiling stack");
}
ProcessRecorder.get().writeRecord(executionRecord);
}
@Nullable
@Override
public T record(@NonNull ExecutionType executionType, @NonNull Block block,
Property... properties) {
List propertyList = properties == null
? ImmutableList.of()
: ImmutableList.copyOf(properties);
return record(executionType, block, propertyList);
}
@Nullable
@Override
public T record(@NonNull ExecutionType executionType, @NonNull Block block,
@NonNull List properties) {
long thisRecordId = ProcessRecorder.allocateRecordId();
// am I a child ?
Long parentId = recordStacks.get().peek();
long startTimeInMs = System.currentTimeMillis();
final PartialRecord currentRecord = new PartialRecord(executionType,
thisRecordId, parentId == null ? 0 : parentId,
startTimeInMs, properties);
recordStacks.get().push(thisRecordId);
try {
return block.call();
} catch (Exception e) {
block.handleException(e);
} finally {
// pop this record from the stack.
if (recordStacks.get().pop() != currentRecord.recordId) {
logger.log(Level.SEVERE, "Profiler stack corrupted");
}
ProcessRecorder.get().writeRecord(
new ExecutionRecord(currentRecord.recordId,
currentRecord.parentRecordId,
currentRecord.startTimeInMs,
System.currentTimeMillis() - currentRecord.startTimeInMs,
currentRecord.executionType,
currentRecord.extraArgs));
}
// we always return null when an exception occurred and was not rethrown.
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy