Merge "Chrome: Change string table indices to unsigned"
diff --git a/README.chromium b/README.chromium
index a8a7cb6..33541c5 100644
--- a/README.chromium
+++ b/README.chromium
@@ -2,7 +2,7 @@
URL: https://android.googlesource.com/platform/external/perfetto/
Version: unknown
License: Apache2
-License File: MODULE_LICENSE_APACHE2
+License File: NOTICE
Security Critical: yes
License Android Compatible: yes
Description: Performance instrumentation and logging for Google client platforms
diff --git a/include/perfetto/base/file_utils.h b/include/perfetto/base/file_utils.h
index e23add1..3126b7c 100644
--- a/include/perfetto/base/file_utils.h
+++ b/include/perfetto/base/file_utils.h
@@ -19,12 +19,27 @@
#include <string>
+#include "perfetto/base/build_config.h"
+
namespace perfetto {
namespace base {
bool ReadFileDescriptor(int fd, std::string* out);
bool ReadFile(const std::string& path, std::string* out);
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+// Call write until all data is written or an error is detected.
+//
+// man 2 write:
+// If a write() is interrupted by a signal handler before any bytes are
+// written, then the call fails with the error EINTR; if it is
+// interrupted after at least one byte has been written, the call
+// succeeds, and returns the number of bytes written.
+ssize_t WriteAll(int fd, const void* buf, size_t count);
+
+#endif
+
} // namespace base
} // namespace perfetto
diff --git a/src/base/debug_crash_stack_trace.cc b/src/base/debug_crash_stack_trace.cc
index c9cc762..3220d9d 100644
--- a/src/base/debug_crash_stack_trace.cc
+++ b/src/base/debug_crash_stack_trace.cc
@@ -28,6 +28,7 @@
#include <unwind.h>
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
// Some glibc headers hit this when using signals.
#pragma GCC diagnostic push
@@ -66,7 +67,7 @@
template <typename T>
void Print(const T& str) {
- write(STDERR_FILENO, str, sizeof(str));
+ perfetto::base::WriteAll(STDERR_FILENO, str, sizeof(str));
}
template <typename T>
@@ -74,7 +75,7 @@
for (unsigned i = 0; i < sizeof(n) * 8; i += 4) {
char nibble = static_cast<char>(n >> (sizeof(n) * 8 - i - 4)) & 0x0F;
char c = (nibble < 10) ? '0' + nibble : 'A' + nibble - 10;
- write(STDERR_FILENO, &c, 1);
+ perfetto::base::WriteAll(STDERR_FILENO, &c, 1);
}
}
@@ -198,14 +199,16 @@
// might be moved.
g_demangled_name = demangled;
}
- write(STDERR_FILENO, sym.sym_name, strlen(sym.sym_name));
+ perfetto::base::WriteAll(STDERR_FILENO, sym.sym_name,
+ strlen(sym.sym_name));
} else {
Print("0x");
PrintHex(frames[i]);
}
if (sym.file_name[0]) {
Print("\n ");
- write(STDERR_FILENO, sym.file_name, strlen(sym.file_name));
+ perfetto::base::WriteAll(STDERR_FILENO, sym.file_name,
+ strlen(sym.file_name));
}
Print("\n");
}
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 6487dbd..89440b1 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -20,6 +20,9 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <unistd.h>
+#endif
namespace perfetto {
namespace base {
@@ -60,5 +63,23 @@
return ReadFileDescriptor(*fd, out);
}
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+ssize_t WriteAll(int fd, const void* buf, size_t count) {
+ size_t written = 0;
+ while (written < count) {
+ ssize_t wr = PERFETTO_EINTR(
+ write(fd, static_cast<const char*>(buf) + written, count - written));
+ if (wr == 0)
+ break;
+ if (wr < 0)
+ return wr;
+ written += static_cast<size_t>(wr);
+ }
+ return static_cast<ssize_t>(written);
+}
+
+#endif
+
} // namespace base
} // namespace perfetto
diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc
index 045d6fb..1585e43 100644
--- a/src/base/metatrace.cc
+++ b/src/base/metatrace.cc
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/time.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
@@ -49,7 +50,7 @@
"{\"ts\": %f, \"cat\": \"PERF\", \"ph\": \"%c\", \"name\": "
"\"%s\", \"pid\": %zu},\n",
GetWallTimeNs().count() / 1000.0, type, evt_name, cpu);
- ignore_result(write(fd, json, static_cast<size_t>(len)));
+ ignore_result(WriteAll(fd, json, static_cast<size_t>(len)));
}
} // namespace base
diff --git a/src/base/task_runner_unittest.cc b/src/base/task_runner_unittest.cc
index 19a3dd5..0bde467 100644
--- a/src/base/task_runner_unittest.cc
+++ b/src/base/task_runner_unittest.cc
@@ -27,6 +27,8 @@
#include <thread>
+#include "perfetto/base/file_utils.h"
+
namespace perfetto {
namespace base {
namespace {
@@ -62,7 +64,7 @@
void Write() {
const char b = '?';
- PERFETTO_DCHECK(write(write_fd.get(), &b, 1) == 1);
+ PERFETTO_DCHECK(WriteAll(write_fd.get(), &b, 1) == 1);
}
ScopedFile read_fd;
diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
index fb4f5ff..bb56248 100644
--- a/src/base/unix_socket.cc
+++ b/src/base/unix_socket.cc
@@ -65,7 +65,8 @@
#endif
void ShiftMsgHdr(size_t n, struct msghdr* msg) {
- for (size_t i = 0; i < msg->msg_iovlen; ++i) {
+ using LenType = decltype(msg->msg_iovlen); // Mac and Linux don't agree.
+ for (LenType i = 0; i < msg->msg_iovlen; ++i) {
struct iovec* vec = &msg->msg_iov[i];
if (n < vec->iov_len) {
// We sent a part of this iovec.
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index 887b9ec..531f1e4 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -16,6 +16,7 @@
#include "perfetto/base/unix_socket.h"
+#include <signal.h>
#include <sys/mman.h>
#include <list>
@@ -24,6 +25,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/utils.h"
@@ -351,7 +353,7 @@
auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
ASSERT_TRUE(srv->is_listening());
// Signal the other process that it can connect.
- ASSERT_EQ(1, PERFETTO_EINTR(write(pipes[1], ".", 1)));
+ ASSERT_EQ(1, base::WriteAll(pipes[1], ".", 1));
auto checkpoint = task_runner_.CreateCheckpoint("change_seen_by_server");
EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
.WillOnce(Invoke(
diff --git a/src/base/utils_unittest.cc b/src/base/utils_unittest.cc
index 459b420..1f556d6 100644
--- a/src/base/utils_unittest.cc
+++ b/src/base/utils_unittest.cc
@@ -23,6 +23,8 @@
#include "gtest/gtest.h"
+#include "perfetto/base/file_utils.h"
+
namespace perfetto {
namespace base {
namespace {
@@ -79,7 +81,7 @@
if (pid == 0 /* child */) {
usleep(5000);
kill(parent_pid, SIGUSR2);
- ignore_result(write(pipe_fd[1], "foo\0", 4));
+ ignore_result(WriteAll(pipe_fd[1], "foo\0", 4));
_exit(0);
}
diff --git a/src/ipc/client_impl_unittest.cc b/src/ipc/client_impl_unittest.cc
index 050fcca..61c7768 100644
--- a/src/ipc/client_impl_unittest.cc
+++ b/src/ipc/client_impl_unittest.cc
@@ -23,6 +23,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/unix_socket.h"
#include "perfetto/base/utils.h"
@@ -349,7 +350,8 @@
base::TempFile tx_file = base::TempFile::CreateUnlinked();
static constexpr char kFileContent[] = "shared file";
- base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+ ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+ sizeof(kFileContent));
host_->next_reply_fd = tx_file.fd();
EXPECT_CALL(*host_method, OnInvoke(_, _))
@@ -393,7 +395,8 @@
base::TempFile tx_file = base::TempFile::CreateUnlinked();
static constexpr char kFileContent[] = "shared file";
- base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+ ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+ sizeof(kFileContent));
EXPECT_CALL(*host_method, OnInvoke(_, _))
.WillOnce(Invoke(
[](const Frame::InvokeMethod&, Frame::InvokeMethodReply* reply) {
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index 26b56c8..8247935 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -20,6 +20,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/unix_socket.h"
@@ -330,7 +331,8 @@
cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
auto on_reply_sent = task_runner_->CreateCheckpoint("on_reply_sent");
base::TempFile tx_file = base::TempFile::CreateUnlinked();
- base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+ ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+ sizeof(kFileContent));
EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
.WillOnce(Invoke([on_reply_sent, &tx_file](const RequestProto&,
DeferredBase* reply) {
@@ -370,7 +372,8 @@
static constexpr char kFileContent[] = "shared file";
RequestProto req_args;
base::TempFile tx_file = base::TempFile::CreateUnlinked();
- base::ignore_result(write(tx_file.fd(), kFileContent, sizeof(kFileContent)));
+ ASSERT_EQ(base::WriteAll(tx_file.fd(), kFileContent, sizeof(kFileContent)),
+ sizeof(kFileContent));
cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args, false,
tx_file.fd());
EXPECT_CALL(*cli_, OnInvokeMethodReply(_));
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index d2fb134..8c8f20e 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -396,8 +396,8 @@
sa.sa_handler = [](int) {
PERFETTO_LOG("SIGINT received: disabling tracing");
char one = '1';
- PERFETTO_CHECK(PERFETTO_EINTR(write(g_consumer_cmd->ctrl_c_pipe_wr(), &one,
- sizeof(one))) == 1);
+ PERFETTO_CHECK(base::WriteAll(g_consumer_cmd->ctrl_c_pipe_wr(), &one,
+ sizeof(one)) == 1);
};
sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
#pragma GCC diagnostic pop
diff --git a/src/perfetto_cmd/rate_limiter.cc b/src/perfetto_cmd/rate_limiter.cc
index af7ef7f..2b789af 100644
--- a/src/perfetto_cmd/rate_limiter.cc
+++ b/src/perfetto_cmd/rate_limiter.cc
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/utils.h"
@@ -184,7 +185,7 @@
PERFETTO_CHECK(size < sizeof(buf));
if (!state.SerializeToArray(&buf, static_cast<int>(size)))
return false;
- ssize_t written = PERFETTO_EINTR(write(out_fd.get(), &buf, size));
+ ssize_t written = base::WriteAll(out_fd.get(), &buf, size);
return written >= 0 && static_cast<size_t>(written) == size;
}
diff --git a/src/perfetto_cmd/rate_limiter_unittest.cc b/src/perfetto_cmd/rate_limiter_unittest.cc
index 8a180a9..988c7b9 100644
--- a/src/perfetto_cmd/rate_limiter_unittest.cc
+++ b/src/perfetto_cmd/rate_limiter_unittest.cc
@@ -18,6 +18,7 @@
#include <stdio.h>
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/utils.h"
@@ -71,7 +72,7 @@
void WriteGarbageToFile(const std::string& path) {
base::ScopedFile fd(base::OpenFile(path, O_WRONLY | O_CREAT, 0600));
constexpr char data[] = "Some random bytes.";
- if (write(fd.get(), data, sizeof(data)) != sizeof(data))
+ if (base::WriteAll(fd.get(), data, sizeof(data)) != sizeof(data))
ADD_FAILURE() << "Could not write garbage";
}
diff --git a/src/traced/probes/filesystem/fs_mount_unittest.cc b/src/traced/probes/filesystem/fs_mount_unittest.cc
index da544e8..6c6b489 100644
--- a/src/traced/probes/filesystem/fs_mount_unittest.cc
+++ b/src/traced/probes/filesystem/fs_mount_unittest.cc
@@ -24,6 +24,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/utils.h"
@@ -52,7 +53,8 @@
)";
base::TempFile tmp_file = base::TempFile::Create();
- base::ignore_result(write(tmp_file.fd(), kMounts, sizeof(kMounts)));
+ ASSERT_EQ(base::WriteAll(tmp_file.fd(), kMounts, sizeof(kMounts)),
+ sizeof(kMounts));
std::multimap<BlockDeviceID, std::string> mounts =
ParseMounts(tmp_file.path().c_str());
struct stat dev_stat = {}, root_stat = {};
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 08a1deb..abc5d09 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -29,6 +29,7 @@
#include <utility>
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/time.h"
#include "perfetto/tracing/core/trace_writer.h"
@@ -76,7 +77,7 @@
auto fd = base::OpenFile(path, O_WRONLY);
if (!fd)
return;
- perfetto::base::ignore_result(write(*fd, str, strlen(str)));
+ base::ignore_result(base::WriteAll(*fd, str, strlen(str)));
}
void ClearFile(const char* path) {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 87095cf..cb84dd7 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -46,7 +46,7 @@
void KernelLogWrite(const char* s) {
PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
if (FtraceProcfs::g_kmesg_fd != -1)
- base::ignore_result(write(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
+ base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
}
} // namespace
@@ -224,7 +224,7 @@
base::ScopedFile fd = base::OpenFile(path, O_WRONLY);
if (!fd)
return false;
- ssize_t written = PERFETTO_EINTR(write(fd.get(), str.c_str(), str.length()));
+ ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length());
ssize_t length = static_cast<ssize_t>(str.length());
// This should either fail or write fully.
PERFETTO_CHECK(written == length || written == -1);
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 9cab744..80b924c 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -31,6 +31,7 @@
#include <algorithm>
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/utils.h"
#include "perfetto/tracing/core/consumer.h"
@@ -80,7 +81,7 @@
ssize_t writev(int fd, const struct iovec* iov, int iovcnt) {
ssize_t total_size = 0;
for (int i = 0; i < iovcnt; ++i) {
- ssize_t current_size = write(fd, iov[i].iov_base, iov[i].iov_len);
+ ssize_t current_size = base::WriteAll(fd, iov[i].iov_base, iov[i].iov_len);
if (current_size != static_cast<ssize_t>(iov[i].iov_len))
return -1;
total_size += current_size;
diff --git a/src/tracing/ipc/posix_shared_memory_unittest.cc b/src/tracing/ipc/posix_shared_memory_unittest.cc
index b07cb00..e6739bf 100644
--- a/src/tracing/ipc/posix_shared_memory_unittest.cc
+++ b/src/tracing/ipc/posix_shared_memory_unittest.cc
@@ -25,6 +25,7 @@
#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/base/utils.h"
@@ -69,7 +70,7 @@
base::TempFile tmp_file = base::TempFile::CreateUnlinked();
const int fd_num = tmp_file.fd();
ASSERT_EQ(0, ftruncate(fd_num, base::kPageSize));
- ASSERT_EQ(7, PERFETTO_EINTR(write(fd_num, "foobar", 7)));
+ ASSERT_EQ(7, base::WriteAll(fd_num, "foobar", 7));
std::unique_ptr<PosixSharedMemory> shm =
PosixSharedMemory::AttachToFd(tmp_file.ReleaseFD());
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index fb108c0..5ae4598 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -15,122 +15,17 @@
import {DraftObject} from 'immer';
import {defaultTraceTime, State, Status, TraceTime} from './state';
-import {TimeSpan} from './time';
-
-export interface Action { type: string; }
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function openTraceFromUrl(url: string) {
- return Actions.openTraceFromUrl({
- url,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function openTraceFromFile(file: File) {
- return Actions.openTraceFromFile({
- file,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function addTrack(
- engineId: string, trackKind: string, name: string, config: {}) {
- return Actions.addTrack({
- engineId,
- kind: trackKind,
- name,
- config,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function requestTrackData(
- trackId: string, start: number, end: number, resolution: number) {
- return Actions.reqTrackData({trackId, start, end, resolution});
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function clearTrackDataRequest(trackId: string) {
- return Actions.clearTrackDataReq({trackId});
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function deleteQuery(queryId: string) {
- return Actions.deleteQuery({
- queryId,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function navigate(route: string) {
- return Actions.navigate({
- route,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function moveTrack(trackId: string, direction: 'up'|'down') {
- return Actions.moveTrack({
- trackId,
- direction,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function toggleTrackPinned(trackId: string) {
- return Actions.toggleTrackPinned({
- trackId,
- });
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function setEngineReady(engineId: string, ready = true) {
- return Actions.setEngineReady({engineId, ready});
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function createPermalink() {
- return Actions.createPermalink({requestId: new Date().toISOString()});
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function setPermalink(requestId: string, hash: string) {
- return Actions.setPermalink({requestId, hash});
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function loadPermalink(hash: string) {
- return Actions.loadPermalink({requestId: new Date().toISOString(), hash});
-}
-
-// TODO(hjd): Temporary until the reducer/action refactoring is done.
-export function setState(newState: State) {
- return Actions.setState({newState});
-}
-
-export function setTraceTime(ts: TimeSpan) {
- return Actions.setTraceTime({
- startSec: ts.start,
- endSec: ts.end,
- lastUpdate: Date.now() / 1000,
- });
-}
-
-export function setVisibleTraceTime(ts: TimeSpan) {
- return Actions.setVisibleTraceTime({
- startSec: ts.start,
- endSec: ts.end,
- lastUpdate: Date.now() / 1000,
- });
-}
-
-export function updateStatus(msg: string) {
- return Actions.updateStatus({msg, timestamp: Date.now() / 1000});
-}
type StateDraft = DraftObject<State>;
+
+function clearTraceState(state: StateDraft) {
+ state.traceTime = defaultTraceTime;
+ state.visibleTraceTime = defaultTraceTime;
+ state.pinnedTracks = [];
+ state.scrollingTracks = [];
+}
+
export const StateActions = {
navigate(state: StateDraft, args: {route: string}): void {
@@ -138,12 +33,8 @@
},
openTraceFromFile(state: StateDraft, args: {file: File}): void {
- state.traceTime = defaultTraceTime;
- state.visibleTraceTime = defaultTraceTime;
+ clearTraceState(state);
const id = `${state.nextId++}`;
- // Reset displayed tracks.
- state.pinnedTracks = [];
- state.scrollingTracks = [];
state.engines[id] = {
id,
ready: false,
@@ -153,12 +44,8 @@
},
openTraceFromUrl(state: StateDraft, args: {url: string}): void {
- state.traceTime = defaultTraceTime;
- state.visibleTraceTime = defaultTraceTime;
+ clearTraceState(state);
const id = `${state.nextId++}`;
- // Reset displayed tracks.
- state.pinnedTracks = [];
- state.scrollingTracks = [];
state.engines[id] = {
id,
ready: false,
@@ -292,7 +179,15 @@
},
};
-// A DeferredAction is a bundle of Args and a method name.
+
+// When we are on the frontend side, we don't really want to execute the
+// actions above, we just want to serialize them and marshal their
+// arguments, send them over to the controller side and have them being
+// executed there. The magic below takes care of turning each action into a
+// function that returns the marshaled args.
+
+// A DeferredAction is a bundle of Args and a method name. This is the marshaled
+// version of a StateActions method call.
export interface DeferredAction<Args = {}> {
type: string;
args: Args;
diff --git a/ui/src/controller/permalink_controller.ts b/ui/src/controller/permalink_controller.ts
index 0146c56..0388934 100644
--- a/ui/src/controller/permalink_controller.ts
+++ b/ui/src/controller/permalink_controller.ts
@@ -15,7 +15,7 @@
import * as uuidv4 from 'uuid/v4';
import {assertExists} from '../base/logging';
-import {setPermalink, setState} from '../common/actions';
+import {Actions} from '../common/actions';
import {EngineConfig, State} from '../common/state';
import {Controller} from './controller';
@@ -40,14 +40,14 @@
// if the |link| is not set, this is a request to create a permalink.
if (globals.state.permalink.hash === undefined) {
PermalinkController.createPermalink().then(hash => {
- globals.dispatch(setPermalink(requestId, hash));
+ globals.dispatch(Actions.setPermalink({requestId, hash}));
});
return;
}
// Otherwise, this is a request to load the permalink.
PermalinkController.loadState(globals.state.permalink.hash).then(state => {
- globals.dispatch(setState(state));
+ globals.dispatch(Actions.setState({newState: state}));
this.lastRequestId = state.permalink.requestId;
});
}
diff --git a/ui/src/controller/query_controller.ts b/ui/src/controller/query_controller.ts
index a700f71..14bbf6e 100644
--- a/ui/src/controller/query_controller.ts
+++ b/ui/src/controller/query_controller.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {assertExists} from '../base/logging';
-import {deleteQuery} from '../common/actions';
+import {Actions} from '../common/actions';
import {rawQueryResultColumns, rawQueryResultIter, Row} from '../common/protos';
import {QueryResponse} from '../common/queries';
import {Controller} from './controller';
@@ -37,7 +37,7 @@
this.runQuery(config.query).then(result => {
console.log(`Query ${config.query} took ${result.durationMs} ms`);
globals.publish('QueryResult', {id: this.args.queryId, data: result});
- globals.dispatch(deleteQuery(this.args.queryId));
+ globals.dispatch(Actions.deleteQuery({queryId: this.args.queryId}));
});
this.setState('querying');
break;
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 08b205f..e68d26c 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -16,13 +16,8 @@
import {assertExists, assertTrue} from '../base/logging';
import {
- addTrack,
+ Actions,
DeferredAction,
- navigate,
- setEngineReady,
- setTraceTime,
- setVisibleTraceTime,
- updateStatus
} from '../common/actions';
import {TimeSpan} from '../common/time';
import {QuantizedLoad, ThreadDesc} from '../frontend/globals';
@@ -64,11 +59,17 @@
const engineCfg = assertExists(globals.state.engines[this.engineId]);
switch (this.state) {
case 'init':
- globals.dispatch(setEngineReady(this.engineId, false));
+ globals.dispatch(Actions.setEngineReady({
+ engineId: this.engineId,
+ ready: false,
+ }));
this.loadTrace().then(() => {
- globals.dispatch(setEngineReady(this.engineId, true));
+ globals.dispatch(Actions.setEngineReady({
+ engineId: this.engineId,
+ ready: true,
+ }));
});
- globals.dispatch(updateStatus('Opening trace'));
+ this.updateStatus('Opening trace');
this.setState('loading_trace');
break;
@@ -110,7 +111,7 @@
}
private async loadTrace() {
- globals.dispatch(updateStatus('Creating trace processor'));
+ this.updateStatus('Creating trace processor');
const engineCfg = assertExists(globals.state.engines[this.engineId]);
this.engine = globals.createEngine();
@@ -124,12 +125,12 @@
const arrBuf = reader.readAsArrayBuffer(slice);
await this.engine.parse(new Uint8Array(arrBuf));
const progress = Math.round((off + slice.size) / blob.size * 100);
- globals.dispatch(updateStatus(`${statusHeader} ${progress} %`));
+ this.updateStatus(`${statusHeader} ${progress} %`);
}
} else {
const resp = await fetch(engineCfg.source);
if (resp.status !== 200) {
- globals.dispatch(updateStatus(`HTTP error ${resp.status}`));
+ this.updateStatus(`HTTP error ${resp.status}`);
throw new Error(`fetch() failed with HTTP error ${resp.status}`);
}
// tslint:disable-next-line no-any
@@ -152,7 +153,7 @@
const tElapsed = (nowMs - tStartMs) / 1e3;
let status = `${statusHeader} ${mb.toFixed(1)} MB `;
status += `(${(mb / tElapsed).toFixed(1)} MB/s)`;
- globals.dispatch(updateStatus(status));
+ this.updateStatus(status);
}
if (readRes.done) break;
}
@@ -161,13 +162,18 @@
await this.engine.notifyEof();
const traceTime = await this.engine.getTraceTimeBounds();
+ const traceTimeState = {
+ startSec: traceTime.start,
+ endSec: traceTime.end,
+ lastUpdate: Date.now() / 1000,
+ };
const actions = [
- setTraceTime(traceTime),
- navigate('/viewer'),
+ Actions.setTraceTime(traceTimeState),
+ Actions.navigate({route: '/viewer'}),
];
if (globals.state.visibleTraceTime.lastUpdate === 0) {
- actions.push(setVisibleTraceTime(traceTime));
+ actions.push(Actions.setVisibleTraceTime(traceTimeState));
}
globals.dispatchMultiple(actions);
@@ -178,20 +184,28 @@
}
private async listTracks() {
- globals.dispatch(updateStatus('Loading tracks'));
+ this.updateStatus('Loading tracks');
const engine = assertExists<Engine>(this.engine);
const addToTrackActions: DeferredAction[] = [];
const numCpus = await engine.getNumberOfCpus();
for (let cpu = 0; cpu < numCpus; cpu++) {
- addToTrackActions.push(
- addTrack(this.engineId, CPU_SLICE_TRACK_KIND, `Cpu ${cpu}`, {
- cpu,
- }));
+ addToTrackActions.push(Actions.addTrack({
+ engineId: this.engineId,
+ kind: CPU_SLICE_TRACK_KIND,
+ name: `Cpu ${cpu}`,
+ config: {
+ cpu,
+ }
+ }));
}
- const threadQuery = await engine.query(
- 'select upid, utid, tid, thread.name, max(slices.depth) ' +
- 'from thread inner join slices using(utid) group by utid');
+ const threadQuery = await engine.query(`
+ select upid, utid, tid, thread.name, depth
+ from thread inner join (
+ select utid, max(slices.depth) as depth
+ from slices
+ group by utid
+ ) using(utid)`);
for (let i = 0; i < threadQuery.numRecords; i++) {
const upid = threadQuery.columns[0].longValues![i];
const utid = threadQuery.columns[1].longValues![i];
@@ -199,18 +213,22 @@
let threadName = threadQuery.columns[3].stringValues![i];
threadName += `[${threadId}]`;
const maxDepth = threadQuery.columns[4].longValues![i];
- addToTrackActions.push(
- addTrack(this.engineId, SLICE_TRACK_KIND, threadName, {
- upid: upid as number,
- utid: utid as number,
- maxDepth: maxDepth as number,
- }));
+ addToTrackActions.push(Actions.addTrack({
+ engineId: this.engineId,
+ kind: SLICE_TRACK_KIND,
+ name: threadName,
+ config: {
+ upid: upid as number,
+ utid: utid as number,
+ maxDepth: maxDepth as number,
+ }
+ }));
}
globals.dispatchMultiple(addToTrackActions);
}
private async listThreads() {
- globals.dispatch(updateStatus('Reading thread list'));
+ this.updateStatus('Reading thread list');
const sqlQuery = 'select utid, tid, pid, thread.name, process.name ' +
'from thread inner join process using(upid)';
const threadRows = await assertExists(this.engine).query(sqlQuery);
@@ -231,9 +249,9 @@
const numSteps = 100;
const stepSec = traceTime.duration / numSteps;
for (let step = 0; step < numSteps; step++) {
- globals.dispatch(updateStatus(
+ this.updateStatus(
'Loading overview ' +
- `${Math.round((step + 1) / numSteps * 1000) / 10}%`));
+ `${Math.round((step + 1) / numSteps * 1000) / 10}%`);
const startSec = traceTime.start + step * stepSec;
const startNs = Math.floor(startSec * 1e9);
const endSec = startSec + stepSec;
@@ -270,4 +288,11 @@
globals.publish('OverviewData', slicesData);
} // for (step ...)
}
+
+ private updateStatus(msg: string): void {
+ globals.dispatch(Actions.updateStatus({
+ msg,
+ timestamp: Date.now() / 1000,
+ }));
+ }
}
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index cbf444c..4340847 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {assertExists} from '../base/logging';
-import {clearTrackDataRequest} from '../common/actions';
+import {Actions} from '../common/actions';
import {Registry} from '../common/registry';
import {TrackState} from '../common/state';
@@ -55,7 +55,7 @@
run() {
const dataReq = this.trackState.dataReq;
if (dataReq === undefined) return;
- globals.dispatch(clearTrackDataRequest(this.trackId));
+ globals.dispatch(Actions.clearTrackDataReq({trackId: this.trackId}));
this.onBoundsChange(dataReq.start, dataReq.end, dataReq.resolution);
}
}
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index b9aa740..47911ea 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {setVisibleTraceTime} from '../common/actions';
+import {Actions} from '../common/actions';
import {TimeSpan} from '../common/time';
import {globals} from './globals';
@@ -44,8 +44,12 @@
this.pendingGlobalTimeUpdate = this.visibleWindowTime;
if (alreadyPosted) return;
setTimeout(() => {
- globals.dispatch(setVisibleTraceTime(this.pendingGlobalTimeUpdate!));
this._visibleTimeLastUpdate = Date.now() / 1000;
+ globals.dispatch(Actions.setVisibleTraceTime({
+ startSec: this.pendingGlobalTimeUpdate!.start,
+ endSec: this.pendingGlobalTimeUpdate!.end,
+ lastUpdate: this._visibleTimeLastUpdate,
+ }));
this.pendingGlobalTimeUpdate = undefined;
}, 100);
}
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index e370127..ea0c248 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -13,13 +13,13 @@
// limitations under the License.
import {assertExists} from '../base/logging';
-import {Action} from '../common/actions';
+import {DeferredAction} from '../common/actions';
import {createEmptyState, State} from '../common/state';
import {FrontendLocalState} from './frontend_local_state';
import {RafScheduler} from './raf_scheduler';
-type Dispatch = (action: Action) => void;
+type Dispatch = (action: DeferredAction) => void;
type TrackDataStore = Map<string, {}>;
type QueryResultsStore = Map<string, {}>;
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 0b92765..0905de4 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -17,7 +17,7 @@
import * as m from 'mithril';
import {forwardRemoteCalls} from '../base/remote';
-import {loadPermalink} from '../common/actions';
+import {Actions} from '../common/actions';
import {State} from '../common/state';
import {TimeSpan} from '../common/time';
import {globals, QuantizedLoad, ThreadDesc} from './globals';
@@ -120,7 +120,11 @@
// /?s=xxxx for permalinks.
const stateHash = router.param('s');
if (stateHash) {
- globals.dispatch(loadPermalink(stateHash));
+ // TODO(hjd): Should requestId not be set to nextId++ in the controller?
+ globals.dispatch(Actions.loadPermalink({
+ requestId: new Date().toISOString(),
+ hash: stateHash,
+ }));
}
// Prevent pinch zoom.
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 15be904..9329053 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -57,26 +57,29 @@
renderStats: new RunningStatistics(10),
};
- // attrs received in the most recent mithril redraw.
- private attrs?: Attrs;
+ // Attrs received in the most recent mithril redraw. We receive a new vnode
+ // with new attrs on every redraw, and we cache it here so that resize
+ // listeners and canvas redraw callbacks can access it.
+ private attrs: Attrs;
- private canvasOverdrawFactor: number;
private ctx?: CanvasRenderingContext2D;
private onResize: () => void = () => {};
private parentOnScroll: () => void = () => {};
private canvasRedrawer: () => void;
+ get canvasOverdrawFactor() {
+ return this.attrs.doesScroll ? SCROLLING_CANVAS_OVERDRAW_FACTOR : 1;
+ }
+
constructor(vnode: m.CVnode<Attrs>) {
- this.canvasOverdrawFactor =
- vnode.attrs.doesScroll ? SCROLLING_CANVAS_OVERDRAW_FACTOR : 1;
+ this.attrs = vnode.attrs;
this.canvasRedrawer = () => this.redrawCanvas();
globals.rafScheduler.addRedrawCallback(this.canvasRedrawer);
perfDisplay.addContainer(this);
}
oncreate(vnodeDom: m.CVnodeDOM<Attrs>) {
- const attrs = vnodeDom.attrs;
// Save the canvas context in the state.
const canvas =
vnodeDom.dom.querySelector('.main-canvas') as HTMLCanvasElement;
@@ -91,21 +94,18 @@
this.parentWidth = clientRect.width;
this.parentHeight = clientRect.height;
- this.updatePanelHeightsFromDom(vnodeDom);
+ this.readPanelHeightsFromDom(vnodeDom.dom);
(vnodeDom.dom as HTMLElement).style.height = `${this.totalPanelHeight}px`;
- this.canvasHeight = this.getCanvasHeight(attrs.doesScroll);
- this.updateCanvasDimensions(vnodeDom);
+ this.updateCanvasDimensions();
+ this.repositionCanvas();
// Save the resize handler in the state so we can remove it later.
// TODO: Encapsulate resize handling better.
this.onResize = () => {
- const clientRect =
- assertExists(vnodeDom.dom.parentElement).getBoundingClientRect();
- this.parentWidth = clientRect.width;
- this.parentHeight = clientRect.height;
- this.canvasHeight = this.getCanvasHeight(attrs.doesScroll);
- this.updateCanvasDimensions(vnodeDom);
+ this.readParentSizeFromDom(vnodeDom.dom);
+ this.updateCanvasDimensions();
+ this.repositionCanvas();
globals.rafScheduler.scheduleFullRedraw();
};
@@ -113,10 +113,10 @@
window.addEventListener('resize', this.onResize);
// TODO(dproy): Handle change in doesScroll attribute.
- if (vnodeDom.attrs.doesScroll) {
+ if (this.attrs.doesScroll) {
this.parentOnScroll = () => {
- this.scrollTop = vnodeDom.dom.parentElement!.scrollTop;
- this.repositionCanvas(vnodeDom);
+ this.scrollTop = assertExists(vnodeDom.dom.parentElement).scrollTop;
+ this.repositionCanvas();
globals.rafScheduler.scheduleRedraw();
};
vnodeDom.dom.parentElement!.addEventListener(
@@ -134,8 +134,6 @@
}
view({attrs}: m.CVnode<Attrs>) {
- // We receive a new vnode object with new attrs on every mithril redraw. We
- // store the latest attrs so redrawCanvas can use it.
this.attrs = attrs;
const renderPanel = (panel: m.Vnode) => perfDebug() ?
m('.panel', panel, m('.debug-panel-border')) :
@@ -148,25 +146,27 @@
}
onupdate(vnodeDom: m.CVnodeDOM<Attrs>) {
- this.repositionCanvas(vnodeDom);
+ const totalPanelHeightChanged = this.readPanelHeightsFromDom(vnodeDom.dom);
+ const parentSizeChanged = this.readParentSizeFromDom(vnodeDom.dom);
- if (this.updatePanelHeightsFromDom(vnodeDom)) {
+ if (totalPanelHeightChanged) {
(vnodeDom.dom as HTMLElement).style.height = `${this.totalPanelHeight}px`;
}
- // In non-scrolling case, canvas height can change if panel heights changed.
- const canvasHeight = this.getCanvasHeight(vnodeDom.attrs.doesScroll);
- if (this.canvasHeight !== canvasHeight) {
- this.canvasHeight = canvasHeight;
- this.updateCanvasDimensions(vnodeDom);
+ const canvasSizeShouldChange =
+ this.attrs.doesScroll ? parentSizeChanged : totalPanelHeightChanged;
+ if (canvasSizeShouldChange) {
+ this.updateCanvasDimensions();
+ this.repositionCanvas();
}
}
- private updateCanvasDimensions(vnodeDom: m.CVnodeDOM<Attrs>) {
- const canvas =
- assertExists(vnodeDom.dom.querySelector('canvas.main-canvas')) as
- HTMLCanvasElement;
+ private updateCanvasDimensions() {
+ this.canvasHeight = this.attrs.doesScroll ?
+ this.parentHeight * this.canvasOverdrawFactor :
+ this.totalPanelHeight;
const ctx = assertExists(this.ctx);
+ const canvas = assertExists(ctx.canvas);
canvas.style.height = `${this.canvasHeight}px`;
const dpr = window.devicePixelRatio;
ctx.canvas.width = this.parentWidth * dpr;
@@ -174,13 +174,36 @@
ctx.scale(dpr, dpr);
}
- private updatePanelHeightsFromDom(vnodeDom: m.CVnodeDOM<Attrs>): boolean {
+ private repositionCanvas() {
+ const canvas = assertExists(assertExists(this.ctx).canvas);
+ const canvasYStart = this.scrollTop - this.getCanvasOverdrawHeightPerSide();
+ canvas.style.transform = `translateY(${canvasYStart}px)`;
+ }
+
+ /**
+ * Reads dimensions of parent node. Returns true if read dimensions are
+ * different from what was cached in the state.
+ */
+ private readParentSizeFromDom(dom: Element): boolean {
+ const oldWidth = this.parentWidth;
+ const oldHeight = this.parentHeight;
+ const clientRect = assertExists(dom.parentElement).getBoundingClientRect();
+ this.parentWidth = clientRect.width;
+ this.parentHeight = clientRect.height;
+ return this.parentHeight !== oldHeight || this.parentWidth !== oldWidth;
+ }
+
+ /**
+ * Reads dimensions of panels. Returns true if total panel height is different
+ * from what was cached in state.
+ */
+ private readPanelHeightsFromDom(dom: Element): boolean {
const prevHeight = this.totalPanelHeight;
this.panelHeights = [];
this.totalPanelHeight = 0;
- const panels = vnodeDom.dom.querySelectorAll('.panel');
- assertTrue(panels.length === vnodeDom.attrs.panels.length);
+ const panels = dom.querySelectorAll('.panel');
+ assertTrue(panels.length === this.attrs.panels.length);
for (let i = 0; i < panels.length; i++) {
const height = panels[i].getBoundingClientRect().height;
this.panelHeights[i] = height;
@@ -190,19 +213,6 @@
return this.totalPanelHeight !== prevHeight;
}
- private getCanvasHeight(doesScroll: boolean) {
- return doesScroll ? this.parentHeight * this.canvasOverdrawFactor :
- this.totalPanelHeight;
- }
-
- private repositionCanvas(vnodeDom: m.CVnodeDOM<Attrs>) {
- const canvas =
- assertExists(vnodeDom.dom.querySelector('canvas.main-canvas')) as
- HTMLCanvasElement;
- const canvasYStart = this.scrollTop - this.getCanvasOverdrawHeightPerSide();
- canvas.style.transform = `translateY(${canvasYStart}px)`;
- }
-
private overlapsCanvas(yStart: number, yEnd: number) {
return yEnd > 0 && yStart < this.canvasHeight;
}
diff --git a/ui/src/frontend/router.ts b/ui/src/frontend/router.ts
index 98bf553..0830712 100644
--- a/ui/src/frontend/router.ts
+++ b/ui/src/frontend/router.ts
@@ -14,7 +14,7 @@
import * as m from 'mithril';
-import {Action, navigate} from '../common/actions';
+import {Actions, DeferredAction} from '../common/actions';
interface RouteMap {
[route: string]: m.Component;
@@ -25,7 +25,7 @@
export class Router {
constructor(
private defaultRoute: string, private routes: RouteMap,
- private dispatch: (a: Action) => void) {
+ private dispatch: (a: DeferredAction) => void) {
if (!(defaultRoute in routes)) {
throw Error('routes must define a component for defaultRoute.');
}
@@ -57,7 +57,7 @@
if (!(route in this.routes)) {
console.info(
`Route ${route} not known redirecting to ${this.defaultRoute}.`);
- this.dispatch(navigate(this.defaultRoute));
+ this.dispatch(Actions.navigate({route: this.defaultRoute}));
}
}
@@ -68,7 +68,7 @@
navigateToCurrentHash() {
const hashRoute = this.getRouteFromHash();
const newRoute = hashRoute in this.routes ? hashRoute : this.defaultRoute;
- this.dispatch(navigate(newRoute));
+ this.dispatch(Actions.navigate({route: newRoute}));
// TODO(dproy): Handle case when new route has a permalink.
}
diff --git a/ui/src/frontend/router_jsdomtest.ts b/ui/src/frontend/router_jsdomtest.ts
index 2d1c4ce..4b8aadb 100644
--- a/ui/src/frontend/router_jsdomtest.ts
+++ b/ui/src/frontend/router_jsdomtest.ts
@@ -14,7 +14,7 @@
import {dingus} from 'dingusjs';
-import {Action, navigate} from '../common/actions';
+import {Actions, DeferredAction} from '../common/actions';
import {Router} from './router';
@@ -49,7 +49,7 @@
});
test('Set valid route on hash', () => {
- const dispatch = dingus<(a: Action) => void>();
+ const dispatch = dingus<(a: DeferredAction) => void>();
const router = new Router(
'/',
{
@@ -67,19 +67,19 @@
});
test('Redirects to default for invalid route in setRouteOnHash ', () => {
- const dispatch = dingus<(a: Action) => void>();
+ const dispatch = dingus<(a: DeferredAction) => void>();
// const dispatch = () => {console.log("action received")};
const router = new Router('/', {'/': mockComponent}, dispatch);
router.setRouteOnHash('foo');
expect(dispatch.calls.length).toBe(1);
expect(dispatch.calls[0][1].length).toBeGreaterThanOrEqual(1);
- expect(dispatch.calls[0][1][0]).toEqual(navigate('/'));
+ expect(dispatch.calls[0][1][0]).toEqual(Actions.navigate({route: '/'}));
});
test('Navigate on hash change', done => {
- const mockDispatch = (a: Action) => {
- expect(a).toEqual(navigate('/viewer'));
+ const mockDispatch = (a: DeferredAction) => {
+ expect(a).toEqual(Actions.navigate({route: '/viewer'}));
done();
};
new Router(
@@ -93,8 +93,8 @@
});
test('Redirects to default when invalid route set in window location', done => {
- const mockDispatch = (a: Action) => {
- expect(a).toEqual(navigate('/'));
+ const mockDispatch = (a: DeferredAction) => {
+ expect(a).toEqual(Actions.navigate({route: '/'}));
done();
};
@@ -110,20 +110,20 @@
});
test('navigateToCurrentHash with valid current route', () => {
- const dispatch = dingus<(a: Action) => void>();
+ const dispatch = dingus<(a: DeferredAction) => void>();
window.location.hash = '#!/b';
const router =
new Router('/', {'/': mockComponent, '/b': mockComponent}, dispatch);
router.navigateToCurrentHash();
- expect(dispatch.calls[0][1][0]).toEqual(navigate('/b'));
+ expect(dispatch.calls[0][1][0]).toEqual(Actions.navigate({route: '/b'}));
});
test('navigateToCurrentHash with invalid current route', () => {
- const dispatch = dingus<(a: Action) => void>();
+ const dispatch = dingus<(a: DeferredAction) => void>();
window.location.hash = '#!/invalid';
const router = new Router('/', {'/': mockComponent}, dispatch);
router.navigateToCurrentHash();
- expect(dispatch.calls[0][1][0]).toEqual(navigate('/'));
+ expect(dispatch.calls[0][1][0]).toEqual(Actions.navigate({route: '/'}));
});
test('Params parsing', () => {
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index 073814c..543c78c 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -14,13 +14,7 @@
import * as m from 'mithril';
-import {
- Actions,
- createPermalink,
- navigate,
- openTraceFromFile,
- openTraceFromUrl
-} from '../common/actions';
+import {Actions} from '../common/actions';
import {globals} from './globals';
@@ -152,7 +146,7 @@
function openTraceUrl(url: string): (e: Event) => void {
return e => {
e.preventDefault();
- globals.dispatch(openTraceFromUrl(url));
+ globals.dispatch(Actions.openTraceFromUrl({url}));
};
}
@@ -161,22 +155,25 @@
throw new Error('Not an input element');
}
if (!e.target.files) return;
- globals.dispatch(openTraceFromFile(e.target.files[0]));
+ globals.dispatch(Actions.openTraceFromFile({file: e.target.files[0]}));
}
function navigateHome(e: Event) {
e.preventDefault();
- globals.dispatch(navigate('/'));
+ globals.dispatch(Actions.navigate({route: '/'}));
}
function navigateRecord(e: Event) {
e.preventDefault();
- globals.dispatch(navigate('/record'));
+ globals.dispatch(Actions.navigate({route: '/record'}));
}
function dispatchCreatePermalink(e: Event) {
e.preventDefault();
- globals.dispatch(createPermalink());
+ // TODO(hjd): Should requestId not be set to nextId++ in the controller?
+ globals.dispatch(Actions.createPermalink({
+ requestId: new Date().toISOString(),
+ }));
}
export class Sidebar implements m.ClassComponent {
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 6e1acdb..acb4d80 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -14,7 +14,7 @@
import * as m from 'mithril';
-import {Actions, deleteQuery} from '../common/actions';
+import {Actions} from '../common/actions';
import {QueryResponse} from '../common/queries';
import {EngineConfig} from '../common/state';
@@ -28,7 +28,7 @@
function clearOmniboxResults() {
globals.queryResults.delete(QUERY_ID);
- globals.dispatch(deleteQuery(QUERY_ID));
+ globals.dispatch(Actions.deleteQuery({queryId: QUERY_ID}));
}
function onKeyDown(e: Event) {
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index a12024d..9386985 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -14,8 +14,7 @@
import * as m from 'mithril';
-import {moveTrack, toggleTrackPinned} from '../common/actions';
-import {Action} from '../common/actions';
+import {Actions, DeferredAction} from '../common/actions';
import {TrackState} from '../common/state';
import {globals} from './globals';
@@ -41,15 +40,17 @@
'.track-shell',
m('h1', attrs.trackState.name),
m(TrackButton, {
- action: moveTrack(attrs.trackState.id, 'up'),
+ action: Actions.moveTrack(
+ {trackId: attrs.trackState.id, direction: 'up'}),
i: 'arrow_upward_alt',
}),
m(TrackButton, {
- action: moveTrack(attrs.trackState.id, 'down'),
+ action: Actions.moveTrack(
+ {trackId: attrs.trackState.id, direction: 'down'}),
i: 'arrow_downward_alt',
}),
m(TrackButton, {
- action: toggleTrackPinned(attrs.trackState.id),
+ action: Actions.toggleTrackPinned({trackId: attrs.trackState.id}),
i: isPinned(attrs.trackState.id) ? 'star' : 'star_border',
}));
}
@@ -87,7 +88,7 @@
}
interface TrackButtonAttrs {
- action: Action;
+ action: DeferredAction;
i: string;
}
class TrackButton implements m.ClassComponent<TrackButtonAttrs> {
diff --git a/ui/src/tracks/chrome_slices/frontend.ts b/ui/src/tracks/chrome_slices/frontend.ts
index 6f64494..0072a75 100644
--- a/ui/src/tracks/chrome_slices/frontend.ts
+++ b/ui/src/tracks/chrome_slices/frontend.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {requestTrackData} from '../../common/actions';
+import {Actions} from '../../common/actions';
import {TrackState} from '../../common/state';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
@@ -58,8 +58,12 @@
const reqEnd = visibleWindowTime.end + visibleWindowTime.duration;
const reqRes = getCurResolution();
this.reqPending = false;
- globals.dispatch(
- requestTrackData(this.trackState.id, reqStart, reqEnd, reqRes));
+ globals.dispatch(Actions.reqTrackData({
+ trackId: this.trackState.id,
+ start: reqStart,
+ end: reqEnd,
+ resolution: reqRes
+ }));
}
renderCanvas(ctx: CanvasRenderingContext2D): void {
diff --git a/ui/src/tracks/cpu_slices/frontend.ts b/ui/src/tracks/cpu_slices/frontend.ts
index ec01b08..e6d0e6c 100644
--- a/ui/src/tracks/cpu_slices/frontend.ts
+++ b/ui/src/tracks/cpu_slices/frontend.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {assertTrue} from '../../base/logging';
-import {requestTrackData} from '../../common/actions';
+import {Actions} from '../../common/actions';
import {TrackState} from '../../common/state';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
@@ -78,8 +78,12 @@
const reqEnd = visibleWindowTime.end + visibleWindowTime.duration;
const reqRes = getCurResolution();
this.reqPending = false;
- globals.dispatch(
- requestTrackData(this.trackState.id, reqStart, reqEnd, reqRes));
+ globals.dispatch(Actions.reqTrackData({
+ trackId: this.trackState.id,
+ start: reqStart,
+ end: reqEnd,
+ resolution: reqRes
+ }));
}
renderCanvas(ctx: CanvasRenderingContext2D): void {