Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perfetto Counter UI and GPU Group #2826

Merged
merged 6 commits into from
Jun 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions gapic/src/main/com/google/gapid/models/Perfetto.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@
*/
package com.google.gapid.models;

import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap;
import static com.google.gapid.rpc.UiErrorCallback.error;
import static com.google.gapid.rpc.UiErrorCallback.success;
import static com.google.gapid.util.Logging.throttleLogRpcError;
import static com.google.gapid.util.MoreFutures.transform;
import static com.google.gapid.util.MoreFutures.transformAsync;
import static com.google.gapid.widgets.Widgets.scheduleIfNotDisposed;
import static java.util.function.Function.identity;
import static java.util.logging.Level.WARNING;

import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gapid.perfetto.TimeSpan;
import com.google.gapid.perfetto.models.CounterInfo;
import com.google.gapid.perfetto.models.ProcessInfo;
import com.google.gapid.perfetto.models.QueryEngine;
import com.google.gapid.perfetto.models.ThreadInfo;
Expand Down Expand Up @@ -82,8 +86,9 @@ protected ListenableFuture<Data> doLoad(Path.Capture source) {
return
transformAsync(withStatus("Examining the trace...", examineTrace(data)), $1 ->
transformAsync(withStatus("Querying threads...", queryThreads(data)), $2 ->
transform(withStatus("Enumerating tracks...", enumerateTracks(data)), $3 ->
data.build())));
transformAsync(withStatus("Querying counters...", queryCounters(data)), $3 ->
transform(withStatus("Enumerating tracks...", enumerateTracks(data)), $4 ->
data.build()))));
}

private static ListenableFuture<Data.Builder> examineTrace(Data.Builder data) {
Expand All @@ -97,6 +102,10 @@ private static ListenableFuture<Data.Builder> queryThreads(Data.Builder data) {
return ThreadInfo.listThreads(data);
}

private static ListenableFuture<Data.Builder> queryCounters(Data.Builder data) {
return CounterInfo.listCounters(data);
}

private static ListenableFuture<Data.Builder> enumerateTracks(Data.Builder data) {
return Tracks.enumerate(data);
}
Expand Down Expand Up @@ -156,16 +165,18 @@ public static class Data {
public final int numCpus;
public final ImmutableMap<Long, ProcessInfo> processes;
public final ImmutableMap<Long, ThreadInfo> threads;
public final ImmutableMap<Long, CounterInfo> counters;
public final TrackConfig tracks;

public Data(QueryEngine queries, TimeSpan traceTime, int numCpus,
ImmutableMap<Long, ProcessInfo> processes, ImmutableMap<Long, ThreadInfo> threads,
TrackConfig tracks) {
ImmutableMap<Long, CounterInfo> counters, TrackConfig tracks) {
this.qe = queries;
this.traceTime = traceTime;
this.numCpus = numCpus;
this.processes = processes;
this.threads = threads;
this.counters = counters;
this.tracks = tracks;
}

Expand All @@ -175,6 +186,8 @@ public static class Builder {
private int numCpus;
private ImmutableMap<Long, ProcessInfo> processes;
private ImmutableMap<Long, ThreadInfo> threads;
private ImmutableMap<Long, CounterInfo> counters;
private ImmutableListMultimap<String, CounterInfo> countersByName;
public final TrackConfig.Builder tracks = new TrackConfig.Builder();

public Builder(QueryEngine qe) {
Expand Down Expand Up @@ -217,8 +230,23 @@ public Builder setThreads(ImmutableMap<Long, ThreadInfo> threads) {
return this;
}

public ImmutableMap<Long, CounterInfo> getCounters() {
return counters;
}

public ImmutableListMultimap<String, CounterInfo> getCountersByName() {
return countersByName;
}

public Builder setCounters(ImmutableMap<Long, CounterInfo> counters) {
this.counters = counters;
this.countersByName = counters.values().stream()
.collect(toImmutableListMultimap(c -> c.name, identity()));
return this;
}

public Data build() {
return new Data(qe, traceTime, numCpus, processes, threads, tracks.build());
return new Data(qe, traceTime, numCpus, processes, threads, counters, tracks.build());
}
}
}
Expand Down
58 changes: 58 additions & 0 deletions gapic/src/main/com/google/gapid/perfetto/models/CounterInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2019 Google Inc.
*
* 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.google.gapid.perfetto.models;

import static com.google.gapid.util.MoreFutures.transform;

import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.gapid.models.Perfetto;

public class CounterInfo {
private static final String LIST_SQL =
"select counter_id, name, ref, ref_type, min(value), max(value) " +
"from counter_definitions left join counter_values using (counter_id) " +
"group by counter_id";

public final long id;
public final String name;
public final long ref;
public final String refType;
public final double min;
public final double max;

public CounterInfo(long id, String name, long ref, String refType, double min, double max) {
this.id = id;
this.name = name;
this.ref = ref;
this.refType = refType;
this.min = min;
this.max = max;
}

private CounterInfo(QueryEngine.Row row) {
this(row.getLong(0), row.getString(1), row.getLong(2), row.getString(3), row.getDouble(4),
row.getDouble(5));
}

public static ListenableFuture<Perfetto.Data.Builder> listCounters(Perfetto.Data.Builder data) {
return transform(data.qe.query(LIST_SQL), res -> {
ImmutableMap.Builder<Long, CounterInfo> counters = ImmutableMap.builder();
res.forEachRow((i, r) -> counters.put(r.getLong(0), new CounterInfo(r)));
return data.setCounters(counters.build());
});
}
}
229 changes: 229 additions & 0 deletions gapic/src/main/com/google/gapid/perfetto/models/CounterTrack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* Copyright (C) 2019 Google Inc.
*
* 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.google.gapid.perfetto.models;

import static com.google.gapid.perfetto.models.QueryEngine.createSpan;
import static com.google.gapid.perfetto.models.QueryEngine.createView;
import static com.google.gapid.perfetto.models.QueryEngine.createWindow;
import static com.google.gapid.perfetto.models.QueryEngine.dropTable;
import static com.google.gapid.perfetto.models.QueryEngine.dropView;
import static com.google.gapid.util.MoreFutures.transform;
import static com.google.gapid.util.MoreFutures.transformAsync;
import static java.lang.String.format;

import com.google.common.util.concurrent.ListenableFuture;
import com.google.gapid.perfetto.TimeSpan;
import com.google.gapid.perfetto.views.CountersSelectionView;
import com.google.gapid.perfetto.views.State;

import org.eclipse.swt.widgets.Composite;

import java.util.Arrays;

public class CounterTrack extends Track<CounterTrack.Data> {
private static final String VIEW_SQL =
"select ts, lead(ts) over (order by ts) - ts dur, value " +
"from counter_values where counter_id = %d";
private static final String SUMMARY_SQL =
"select min(ts), max(ts + dur), avg(value) from %s group by quantum_ts";
private static final String COUNTER_SQL = "select ts, ts + dur, value from %s";
private static final String RANGE_SQL =
"select ts, ts + dur, value from %s " +
"where ts + dur >= %d and ts <= %d order by ts";

private final long id;
private final double min;
private final double max;

public CounterTrack(long id, double min, double max) {
super("counter_" + id);
this.id = id;
this.min = min;
this.max = max;
}

public double getMin() {
return min;
}

public double getMax() {
return max;
}

@Override
protected ListenableFuture<?> initialize(QueryEngine qe) {
String vals = tableName("vals");
String span = tableName("span");
String window = tableName("window");
return qe.queries(
dropTable(span),
dropTable(window),
dropView(vals),
createView(vals, viewSql()),
createWindow(window),
createSpan(span, vals + ", " + window));
}

private String viewSql() {
return format(VIEW_SQL, id);
}

@Override
protected ListenableFuture<Data> computeData(QueryEngine qe, DataRequest req) {
Window win = Window.compute(req, 5);
return transformAsync(win.update(qe, tableName("window")), $ -> computeData(qe, req, win));
}

private ListenableFuture<Data> computeData(QueryEngine qe, DataRequest req, Window win) {
return transform(qe.query(win.quantized ? summarySql() : counterSQL()), res -> {
int rows = res.getNumRows();
Data data = new Data(req, new long[rows + 1], new double[rows + 1]);
res.forEachRow((i, r) -> {
data.ts[i] = r.getLong(0);
data.values[i] = r.getDouble(2);
});
data.ts[rows] = res.getLong(rows - 1, 1, 0);
data.values[rows] = data.values[rows - 1];
return data;
});
}

private String summarySql() {
return format(SUMMARY_SQL, tableName("span"));
}

private String counterSQL() {
return format(COUNTER_SQL, tableName("span"));
}

public ListenableFuture<Data> getValues(QueryEngine qe, TimeSpan ts) {
return transform(qe.query(rangeSql(ts)), res -> {
int rows = res.getNumRows();
Data data = new Data(null, new long[rows], new double[rows]);
res.forEachRow((i, r) -> {
data.ts[i] = r.getLong(0);
data.values[i] = r.getDouble(2);
});
return data;
});
}

private String rangeSql(TimeSpan ts) {
return format(RANGE_SQL, tableName("vals"), ts.start, ts.end);
}

public static class Data extends Track.Data {
public final long[] ts;
public final double[] values;

public Data(DataRequest request, long[] ts, double[] values) {
super(request);
this.ts = ts;
this.values = values;
}
}

public static class Values implements Selection, Selection.CombiningBuilder.Combinable<Values> {
public final long[] ts;
public final String[] names;
public final double[][] values;

public Values(String name, Data data) {
this.ts = data.ts;
this.names = new String[] { name };
this.values = new double[][] { data.values };
}

private Values(long[] ts, String[] names, double[][] values) {
this.ts = ts;
this.names = names;
this.values = values;
}

@Override
public String getTitle() {
return "Counters";
}

@Override
public Composite buildUi(Composite parent, State state) {
return new CountersSelectionView(parent, state, this);
}

@Override
public Values combine(Values other) {
long[] newTs = combineTs(ts, other.ts);

double[][] newValues = new double[names.length + other.names.length][newTs.length];
for (int i = 0, me = 0, them = 0; i < newTs.length; i++) {
long rTs = newTs[i], meTs = ts[me], themTs = other.ts[them];
if (rTs == meTs) {
for (int n = 0; n < names.length; n++) {
newValues[n][i] = values[n][me];
}
me = Math.min(me + 1, ts.length - 1);
} else if (i > 0) {
for (int n = 0; n < names.length; n++) {
newValues[n][i] = newValues[n][i - 1];
}
}

if (rTs == themTs) {
for (int n = 0; n < other.names.length; n++) {
newValues[n + names.length][i] = other.values[n][them];
}
them = Math.min(them + 1, other.ts.length - 1);
} else if (i > 0) {
for (int n = 0; n < other.names.length; n++) {
newValues[names.length + n][i] = newValues[names.length + n][i - 1];
}
}
}

String[] newNames = Arrays.copyOf(names, names.length + other.names.length);
System.arraycopy(other.names, 0, newNames, names.length, other.names.length);
return new Values(newTs, newNames, newValues);
}

private static long[] combineTs(long[] a, long[] b) {
long[] r = new long[a.length + b.length];
int ai = 0, bi = 0, ri = 0;
for (; ai < a.length && bi < b.length; ri++) {
long av = a[ai], bv = b[bi];
if (av == bv) {
r[ri] = av;
ai++;
bi++;
} else if (av < bv) {
r[ri] = av;
ai++;
} else {
r[ri] = bv;
bi++;
}
}
// One of these copies does nothing.
System.arraycopy(a, ai, r, ri, a.length - ai);
System.arraycopy(b, bi, r, ri, b.length - bi);
return Arrays.copyOf(r, ri + a.length - ai + b.length - bi); // Truncate array.
}

@Override
public Selection build() {
return this;
}
}
}
Loading