Skip to content

Commit

Permalink
Show the server status in the UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
pmuetschard committed Feb 2, 2019
1 parent f8d8de7 commit edf97a6
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 10 deletions.
26 changes: 25 additions & 1 deletion gapic/src/main/com/google/gapid/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.google.gapid.util.MacApplication;
import com.google.gapid.util.Messages;
import com.google.gapid.util.OS;
import com.google.gapid.util.StatusWatcher;
import com.google.gapid.util.UpdateWatcher;
import com.google.gapid.views.StatusBar;
import com.google.gapid.widgets.CopyPaste;
Expand Down Expand Up @@ -76,7 +77,7 @@ public class MainWindow extends ApplicationWindow {
private final Theme theme;
private Composite mainArea;
private LoadingScreen loadingScreen;
private StatusBar statusBar;
protected StatusBar statusBar;

public MainWindow(Settings settings, Theme theme) {
super(null);
Expand All @@ -94,6 +95,7 @@ public void showLoadingMessage(String status) {
public void initMainUi(Client client, Models models, Widgets widgets) {
Shell shell = getShell();

showLoadingMessage("Setting up UI...");
initMenus(client, models, widgets);

LoadablePanel<GraphicsTraceView> mainUi = new LoadablePanel<GraphicsTraceView>(
Expand Down Expand Up @@ -125,8 +127,16 @@ public void onCaptureLoaded(Message error) {
file -> models.capture.loadCapture(new File(file)));
}

if (settings.autoCheckForUpdates) {
// Only show the status message if we're actually checking for updates. watchForUpdates only
//schedules a periodic check to see if we should check for updates and if so, checks.
showLoadingMessage("Watching for updates...");
}
watchForUpdates(client, models);

showLoadingMessage("Tracking server status...");
trackServerStatus(client);

showLoadingMessage("Ready! Please open or capture a trace file.");
}

Expand All @@ -140,6 +150,20 @@ private void watchForUpdates(Client client, Models models) {
});
}

private void trackServerStatus(Client client) {
new StatusWatcher(client, new StatusWatcher.Listener() {
@Override
public void onStatus(String status) {
scheduleIfNotDisposed(statusBar, () -> statusBar.setServerStatus(status));
}

@Override
public void onHeap(long heap) {
scheduleIfNotDisposed(statusBar, () -> statusBar.setServerHeapSize(heap));
}
});
}

@Override
protected void configureShell(Shell shell) {
shell.setText(Messages.WINDOW_TITLE);
Expand Down
9 changes: 9 additions & 0 deletions gapic/src/main/com/google/gapid/server/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,15 @@ public ListenableFuture<Void> streamLog(Consumer<Log.Message> onLogMessage) {
return client.streamLog(onLogMessage);
}

public ListenableFuture<Void> streamStatus(
float memoryS, float statusS, Consumer<Service.ServerStatusResponse> onStatus) {
LOG.log(FINE, "RPC->streamStatus({}, {})", new Object[] { memoryS, statusS });
return client.streamStatus(Service.ServerStatusRequest.newBuilder()
.setMemorySnapshotInterval(memoryS)
.setStatusUpdateFrequency(statusS)
.build(), onStatus);
}

public ListenableFuture<Void> streamSearch(
Service.FindRequest request, Consumer<Service.FindResponse> onResult) {
LOG.log(FINE, "RPC->find({0})", request);
Expand Down
2 changes: 2 additions & 0 deletions gapic/src/main/com/google/gapid/server/GapidClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public ListenableFuture<Service.UpdateSettingsResponse> updateSettings(
Service.UpdateSettingsRequest request);

public ListenableFuture<Void> streamLog(Consumer<Log.Message> onLogMessage);
public ListenableFuture<Void> streamStatus(
Service.ServerStatusRequest request, Consumer<Service.ServerStatusResponse> onStatus);
public ListenableFuture<Void> streamSearch(
Service.FindRequest request, Consumer<Service.FindResponse> onResult);
public StreamSender<Service.TraceRequest> streamTrace(
Expand Down
9 changes: 9 additions & 0 deletions gapic/src/main/com/google/gapid/server/GapidClientGrpc.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ public ListenableFuture<Void> streamLog(Consumer<Log.Message> onLogMessage) {
return handler.future;
}

@Override
public ListenableFuture<Void> streamStatus(
Service.ServerStatusRequest request, Consumer<Service.ServerStatusResponse> onStatus) {
StreamHandler<Service.ServerStatusResponse> handler = StreamHandler.wrap(onStatus);
stub.status(request, handler);
return handler.future;
}


@Override
public ListenableFuture<Void> streamSearch(
Service.FindRequest request, Consumer<Service.FindResponse> onResult) {
Expand Down
197 changes: 197 additions & 0 deletions gapic/src/main/com/google/gapid/util/StatusWatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* Copyright (C) 2017 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.util;

import com.google.common.collect.Maps;
import com.google.gapid.proto.service.Service;
import com.google.gapid.server.Client;

import java.util.HashMap;
import java.util.LinkedHashMap;

/**
* Utility class for monitoring the server status.
*/
public class StatusWatcher {
private static final float STATUS_UPDATE_INTERVAL_S = 0.5f;
private static final float MEMORY_UPDATE_INTERVAL_S = 1.0f;

private final Listener listener;
private final Task root = Task.newRoot();
private final HashMap<Long, Task> tasks = Maps.newHashMap();
private String shownSummary = "";

public StatusWatcher(Client client, Listener listener) {
this.listener = listener;

client.streamStatus(MEMORY_UPDATE_INTERVAL_S, STATUS_UPDATE_INTERVAL_S, update -> {
switch (update.getResCase()) {
case TASK:
onTaskUpdate(update.getTask());
break;
case MEMORY:
onMemoryUpdate(update.getMemory());
break;
default:
// Ignore.
}
});
}

private void onTaskUpdate(Service.TaskUpdate update) {
String summary;
synchronized (this) {
switch(update.getStatus()) {
case STARTING:
Task parent = tasks.getOrDefault(update.getParent(), root);
Task child = new Task(update, parent);
tasks.put(update.getId(), child);
parent.addChild(child);
break;
case FINISHED:
tasks.getOrDefault(update.getId(), root).remove();
break;
case PROGRESS:
tasks.getOrDefault(update.getId(), root).setProgress(update.getCompletePercent());
break;
case BLOCKED:
tasks.getOrDefault(update.getId(), root).setBlocked(true);
break;
case UNBLOCKED:
tasks.getOrDefault(update.getId(), root).setBlocked(false);
break;
default:
// Ignore.
return;
}

summary = root.getFirstChild(Task.State.RUNNING).getStatusLabel();
if (shownSummary.equals(summary)) {
return;
}
shownSummary = summary;
}

listener.onStatus(summary);
}

private void onMemoryUpdate(Service.MemoryStatus update) {
listener.onHeap(update.getTotalHeap());
}

public static interface Listener {
public void onStatus(String status);
public void onHeap(long heap);
}

private static class Task {
private final long id;
private final Task parent;
private final String name;
private final LinkedHashMap<Long, Task> children = Maps.newLinkedHashMap();
private State state;
private int progress;

public Task(long id, Task parent, String name, State state, int progress) {
this.id = id;
this.parent = parent;
this.name = name;
this.state = state;
this.progress = progress;
}

public Task(Service.TaskUpdate update, Task parent) {
this(update.getId(), parent, update.getName(), getState(update), update.getCompletePercent());
}

private static State getState(Service.TaskUpdate update) {
return (update.getBackground()) ? State.BACKGROUND : State.RUNNING;
}

public static Task newRoot() {
return new Task(-1, null, null, null, 0) {
@Override
public void setBlocked(boolean newVal) {
// Don't do anything.
}

@Override
public void remove() {
// Don't do anything.
}

@Override
public String getLabel() {
return "";
}
};
}

public void setBlocked(boolean blocked){
if (state != State.BACKGROUND) {
state = blocked ? State.BLOCKED: State.RUNNING;
parent.setBlocked(blocked);
}
}

public void setProgress(int progress) {
this.progress = progress;
}

public void addChild(Task child) {
children.put(child.id, child);
}

public void remove() {
parent.children.remove(id);
}

public Task getFirstChild(State inState) {
for (Task child : children.values()) {
if (child.state == inState) {
return child;
}
}
return this;
}

public Task getLeftMostDecendant(State inState) {
for (Task child : children.values()) {
if (child.state == inState) {
return child.getLeftMostDecendant(inState);
}
}
return this;
}

public String getLabel() {
return (progress == 0) ? name : name + "<" + progress + "%>";
}

public String getStatusLabel() {
Task leaf = getLeftMostDecendant(State.RUNNING);
if (leaf == this) {
return getLabel();
} else {
return getLabel() + " ... " + leaf.getLabel();
}
}

private static enum State {
BACKGROUND, RUNNING, BLOCKED,
}
}
}
Loading

0 comments on commit edf97a6

Please sign in to comment.