Merge "cts: test profileability of platform processes"
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index bd80e8a..1f75415 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -151,7 +151,7 @@
MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'Running'), 0
),
'runnable_dur_ns', IFNULL(
- MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*'), 0
+ MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id), 0
),
'uninterruptible_sleep_dur_ns', IFNULL(
MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'D*'), 0
@@ -320,7 +320,7 @@
UNION ALL
SELECT 'Main Thread - Time spent in Runnable state'
AS slow_cause
- WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*') > 100e6
+ WHERE MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id) > 100e6
UNION ALL
SELECT 'Main Thread - Time spent in interruptible sleep state'
@@ -361,7 +361,7 @@
SELECT 'Potential CPU contention with '
|| MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id)
AS slow_cause
- WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*') > 100e6
+ WHERE MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id) > 100e6
AND MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id) IS NOT NULL
UNION ALL
diff --git a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
index b85d40b..b3939bb 100644
--- a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
+++ b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
@@ -66,6 +66,17 @@
'
);
+-- Given a launch id, returns the aggregate sum of time spent in runnable state
+-- by the main thread of the process being started up.
+SELECT CREATE_FUNCTION(
+ 'MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(startup_id INT)',
+ 'INT',
+ '
+ SELECT IFNULL(MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE($startup_id, "R"), 0)
+ + IFNULL(MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE($startup_id, "R+"), 0);
+ '
+);
+
-- Given a launch id, thread state and io_wait value, returns the aggregate sum
-- of time spent in that state by the main thread of the process being started up.
SELECT CREATE_FUNCTION(
diff --git a/src/trace_processor/util/bump_allocator.cc b/src/trace_processor/util/bump_allocator.cc
index 1c00a8c..4cbf928 100644
--- a/src/trace_processor/util/bump_allocator.cc
+++ b/src/trace_processor/util/bump_allocator.cc
@@ -98,11 +98,14 @@
return to_erase_chunks;
}
-uint32_t BumpAllocator::PastEndSerializedId() {
+BumpAllocator::AllocId BumpAllocator::PastTheEndId() {
if (chunks_.empty()) {
- return AllocId{erased_front_chunks_count_, 0}.Serialize();
+ return AllocId{erased_front_chunks_count_, 0};
}
- return AllocId{LastChunkIndex(), chunks_.back().bump_offset}.Serialize();
+ if (chunks_.back().bump_offset == kChunkSize) {
+ return AllocId{LastChunkIndex() + 1, 0};
+ }
+ return AllocId{LastChunkIndex(), chunks_.back().bump_offset};
}
base::Optional<BumpAllocator::AllocId> BumpAllocator::TryAllocInLastChunk(
diff --git a/src/trace_processor/util/bump_allocator.h b/src/trace_processor/util/bump_allocator.h
index cd5e593..41ab0b1 100644
--- a/src/trace_processor/util/bump_allocator.h
+++ b/src/trace_processor/util/bump_allocator.h
@@ -22,6 +22,7 @@
#include <cstring>
#include <limits>
#include <memory>
+#include <tuple>
#include "perfetto/ext/base/circular_queue.h"
#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/utils.h"
@@ -80,17 +81,13 @@
uint32_t chunk_index : kChunkIndexAllocIdBits;
uint32_t chunk_offset : kChunkOffsetAllocIdBits;
- uint32_t Serialize() const {
- return static_cast<uint32_t>(chunk_index) << kChunkOffsetAllocIdBits |
- chunk_offset;
+ // Comparision operators mainly for sorting.
+ bool operator<(const AllocId& other) const {
+ return std::tie(chunk_index, chunk_offset) <
+ std::tie(other.chunk_index, other.chunk_offset);
}
-
- static AllocId FromSerialized(uint32_t serialized) {
- AllocId id;
- id.chunk_index = serialized >> kChunkOffsetAllocIdBits;
- id.chunk_offset = serialized;
- return id;
- }
+ bool operator>=(const AllocId& other) const { return !(*this < other); }
+ bool operator>(const AllocId& other) const { return other < *this; }
};
static_assert(sizeof(AllocId) == sizeof(uint32_t),
"AllocId should be 32-bit in size to allow serialization");
@@ -142,7 +139,7 @@
// Returns a "past the end" serialized AllocId i.e. a serialized value
// greater than all previously returned AllocIds.
- uint32_t PastEndSerializedId();
+ AllocId PastTheEndId();
// Returns the number of erased chunks from the start of this allocator.
//
diff --git a/src/trace_processor/util/bump_allocator_unittest.cc b/src/trace_processor/util/bump_allocator_unittest.cc
index 4608be6..38490fd 100644
--- a/src/trace_processor/util/bump_allocator_unittest.cc
+++ b/src/trace_processor/util/bump_allocator_unittest.cc
@@ -70,26 +70,13 @@
allocator_.EraseFrontFreeChunks();
}
-TEST_F(BumpAllocatorUnittest, Serialize) {
- BumpAllocator::AllocId id = allocator_.Alloc(8);
- ASSERT_EQ(id.Serialize(), 0u);
- ASSERT_EQ(allocator_.PastEndSerializedId(), 8u);
+TEST_F(BumpAllocatorUnittest, PastEndOnChunkBoundary) {
+ BumpAllocator::AllocId id = allocator_.Alloc(BumpAllocator::kChunkSize);
+ BumpAllocator::AllocId past_end = allocator_.PastTheEndId();
+ ASSERT_GT(past_end, id);
+ ASSERT_EQ(past_end.chunk_index, 1u);
+ ASSERT_EQ(past_end.chunk_offset, 0u);
allocator_.Free(id);
-
- id = allocator_.Alloc(8);
- ASSERT_EQ(id.Serialize(), 8u);
- allocator_.Free(id);
-
- id = allocator_.Alloc(BumpAllocator::kChunkSize);
- ASSERT_EQ(id.Serialize(), BumpAllocator::kChunkSize);
- allocator_.Free(id);
-}
-
-TEST_F(BumpAllocatorUnittest, HighNumberSerialize) {
- BumpAllocator::AllocId id = BumpAllocator::AllocId::FromSerialized(1138352);
- ASSERT_EQ(id.chunk_index, 1138352 / BumpAllocator::kChunkSize);
- ASSERT_EQ(id.chunk_offset, 1138352 % BumpAllocator::kChunkSize);
- ASSERT_EQ(id.Serialize(), 1138352u);
}
TEST_F(BumpAllocatorUnittest, EraseFrontAccounting) {
@@ -105,8 +92,7 @@
AllocateWriteReadAndFree(8);
allocator_.EraseFrontFreeChunks();
- auto past_id =
- BumpAllocator::AllocId::FromSerialized(allocator_.PastEndSerializedId());
+ auto past_id = allocator_.PastTheEndId();
ASSERT_EQ(past_id.chunk_index, 1u);
ASSERT_EQ(past_id.chunk_offset, 0u);
diff --git a/test/trace_processor/diff_tests/startup/android_startup.out b/test/trace_processor/diff_tests/startup/android_startup.out
index 752db7b..f804a34 100644
--- a/test/trace_processor/diff_tests/startup/android_startup.out
+++ b/test/trace_processor/diff_tests/startup/android_startup.out
@@ -8,7 +8,7 @@
dur_ns: 108
main_thread_by_task_state {
running_dur_ns: 10
- runnable_dur_ns: 90
+ runnable_dur_ns: 80
uninterruptible_sleep_dur_ns: 0
interruptible_sleep_dur_ns: 10
uninterruptible_io_sleep_dur_ns: 0
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
index 8b35bb5..132aad8 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
@@ -8,7 +8,7 @@
dur_ns: 108000000000
main_thread_by_task_state {
running_dur_ns: 25000000000
- runnable_dur_ns: 30000000000
+ runnable_dur_ns: 5000000000
uninterruptible_sleep_dur_ns: 0
interruptible_sleep_dur_ns: 0
uninterruptible_io_sleep_dur_ns: 0
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
index 3ed8ce3..2974f88 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
@@ -8,7 +8,7 @@
dur_ns: 108000000000
main_thread_by_task_state {
running_dur_ns: 25000000000
- runnable_dur_ns: 30000000000
+ runnable_dur_ns: 5000000000
uninterruptible_sleep_dur_ns: 0
interruptible_sleep_dur_ns: 0
uninterruptible_io_sleep_dur_ns: 0
diff --git a/test/trace_processor/diff_tests/startup/android_startup_slow.out b/test/trace_processor/diff_tests/startup/android_startup_slow.out
index 333883a..22f2d90 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_slow.out
@@ -8,7 +8,7 @@
dur_ns: 108000000000
main_thread_by_task_state {
running_dur_ns: 10000000000
- runnable_dur_ns: 90000000000
+ runnable_dur_ns: 80000000000
uninterruptible_sleep_dur_ns: 5000000000
interruptible_sleep_dur_ns: 5000000000
uninterruptible_io_sleep_dur_ns: 5000000000
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index c6d03b4..820ee59 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -25,3 +25,4 @@
@import "trace_info_page";
@import "flags_page";
@import "hiring_banner";
+@import "widgets/button";
diff --git a/ui/src/assets/widgets/button.scss b/ui/src/assets/widgets/button.scss
new file mode 100644
index 0000000..4ab5b55
--- /dev/null
+++ b/ui/src/assets/widgets/button.scss
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 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.
+
+@import "theme";
+
+.pf-button {
+ font-family: $pf-font;
+ line-height: 1;
+ user-select: none;
+ color: $pf-primary-foreground;
+ background: $pf-primary-background;
+ transition: background $pf-anim-timing, box-shadow $pf-anim-timing;
+ border-radius: $pf-border-radius;
+ padding: 4px 8px;
+
+ & > .material-icons {
+ font-size: inherit;
+ line-height: inherit;
+ float: left;
+ margin-right: 4px; // Make some room between the icon and label
+ }
+
+ &:hover {
+ background: $pf-primary-background-hover;
+ }
+
+ &:active,
+ &.pf-active {
+ transition: none;
+ background: $pf-primary-background-active;
+ box-shadow: inset 1px 1px 4px #00000040;
+ }
+
+ &[disabled] {
+ background: $pf-primary-background-disabled;
+ color: $pf-primary-foreground-disabled;
+ box-shadow: none;
+ cursor: not-allowed;
+ }
+
+ // Remove default background in minimal mode, showing only the text
+ &.pf-minimal {
+ background: $pf-minimal-background;
+ color: $pf-minimal-foreground;
+
+ &:hover {
+ background: $pf-minimal-background-hover;
+ }
+
+ &:active,
+ &.pf-active {
+ background: $pf-minimal-background-active;
+ }
+
+ &[disabled] {
+ color: $pf-minimal-foreground-disabled;
+ background: $pf-minimal-background-disabled;
+ cursor: not-allowed;
+ }
+ }
+
+ // Reduce padding when compact
+ &.pf-compact {
+ padding: 2px 4px;
+ }
+
+ // Reduce padding when we are icon-only
+ &.pf-icon-only {
+ & > i {
+ margin: 0;
+ }
+
+ padding: 4px 4px;
+
+ &.pf-compact {
+ padding: 0;
+ }
+ }
+}
diff --git a/ui/src/assets/widgets/theme.scss b/ui/src/assets/widgets/theme.scss
new file mode 100644
index 0000000..38d9107
--- /dev/null
+++ b/ui/src/assets/widgets/theme.scss
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 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.
+
+// Standard theme settings for widgets
+
+$pf-font: "Roboto Condensed", sans-serif;
+$pf-border-radius: 3px;
+$pf-anim-timing: 250ms cubic-bezier(0.4, 0, 0.2, 1);
+
+// Here we describe two colour schemes: primary and minimal
+// It is assumed widgets exist on a light background
+// Primary is to be used for things like buttons and checkboxes
+// Minimal is to be used for things like inputs and labels
+// Some controls (i.e. checkboxes) may mix and match both in the same widget.
+// Other controls might use the primary scheme by default, but have a minimal
+// configuration which makes them use the minimal colour scheme.
+
+$pf-primary-foreground: #fff;
+$pf-primary-foreground-disabled: #aaa;
+$pf-primary-background: #3d5688;
+$pf-primary-background-hover: #4966a2;
+$pf-primary-background-active: #243e71;
+$pf-primary-background-disabled: #666;
+
+$pf-minimal-foreground: #19212b;
+$pf-minimal-foreground-disabled: #aaa;
+$pf-minimal-background: none;
+$pf-minimal-background-hover: #0001;
+$pf-minimal-background-active: #0002;
+$pf-minimal-background-disabled: none;
diff --git a/ui/src/frontend/classnames.ts b/ui/src/frontend/classnames.ts
new file mode 100644
index 0000000..b2f6007
--- /dev/null
+++ b/ui/src/frontend/classnames.ts
@@ -0,0 +1,25 @@
+// Copyright (C) 2023 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.
+
+// It's common to want to have a class depending on a boolean flag, in which
+// case we use `flag && className` which evaluates to either false or a string,
+// which is why false is included in definition of ArgType.
+type ArgType = string|false|undefined|ArgType[];
+
+// Join class names together into valid HTML class attributes
+// Falsey elements are ignored
+// Nested arrays are flattened
+export function classNames(...args: ArgType[]): string {
+ return args.flat().filter((x) => x).join(' ');
+}
diff --git a/ui/src/frontend/classnames_unittest.ts b/ui/src/frontend/classnames_unittest.ts
new file mode 100644
index 0000000..29546db
--- /dev/null
+++ b/ui/src/frontend/classnames_unittest.ts
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 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.
+
+import {classNames} from './classnames';
+
+test('classnames', () => {
+ expect(classNames('foo', 'bar')).toEqual('foo bar');
+ expect(classNames('foo', '', 'bar')).toEqual('foo bar');
+ expect(classNames(false, 'foo', 'bar')).toEqual('foo bar');
+ expect(classNames(undefined, 'foo', 'bar')).toEqual('foo bar');
+ expect(classNames('foo', 'bar', ['baz', 'qux'])).toEqual('foo bar baz qux');
+ expect(classNames('foo bar', 'baz')).toEqual('foo bar baz');
+});
+
+test('example usecase with flags', () => {
+ const foo = true;
+ const bar = false;
+ const baz = true;
+ expect(classNames(
+ foo && 'foo',
+ bar && 'bar',
+ baz && 'baz',
+ ))
+ .toEqual('foo baz');
+});
+
+test('example usecase with possibly undefined classnames', () => {
+ let fooClass: string|undefined;
+ const barClass = 'bar';
+ expect(classNames(
+ fooClass,
+ barClass,
+ ))
+ .toEqual('bar');
+});
diff --git a/ui/src/frontend/widgets/button.ts b/ui/src/frontend/widgets/button.ts
new file mode 100644
index 0000000..2e6f3d6
--- /dev/null
+++ b/ui/src/frontend/widgets/button.ts
@@ -0,0 +1,83 @@
+// Copyright (C) 2023 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.
+
+import * as m from 'mithril';
+import {classNames} from '../classnames';
+
+interface CommonAttrs {
+ // Always show the button as if the "active" pseudo class were applied, which
+ // makes the button look permanently pressed.
+ // Useful for when the button represents some toggleable state, such as
+ // showing/hiding a popup menu.
+ // Defaults to false.
+ active?: boolean;
+ // Use minimal padding, reducing the overall size of the button by a few px.
+ // Defaults to false.
+ compact?: boolean;
+ // Reduces button decorations.
+ // Defaults to false.
+ minimal?: boolean;
+ // Make the button appear greyed out block any interaction with it. No events
+ // will be fired.
+ // Defaults to false.
+ disabled?: boolean;
+ // Remaining attributes forwarded to the underlying HTML <button>.
+ [htmlAttrs: string]: any;
+}
+
+interface IconButtonAttrs extends CommonAttrs {
+ // Icon buttons require an icon.
+ icon: string;
+}
+
+interface LabelButtonAttrs extends CommonAttrs {
+ // Label buttons require a label.
+ label: string;
+ // Label buttons can have an optional icon.
+ icon?: string;
+}
+
+export type ButtonAttrs = LabelButtonAttrs|IconButtonAttrs;
+
+export class Button implements m.ClassComponent<ButtonAttrs> {
+ view({attrs}: m.CVnode<ButtonAttrs>) {
+ const {
+ label,
+ icon,
+ active = false,
+ compact = false,
+ minimal = false,
+ disabled = false,
+ ...htmlAttrs
+ } = attrs;
+
+ const classes = classNames(
+ 'pf-button',
+ active && 'pf-active',
+ compact && 'pf-compact',
+ minimal && 'pf-minimal',
+ (icon && !label) && 'pf-icon-only',
+ );
+
+ return m(
+ 'button' + (disabled ? '[disabled]' : ''),
+ {
+ class: classes,
+ ...htmlAttrs,
+ },
+ icon && m('i.material-icons', icon),
+ label || '\u200B', // Zero width space keeps button in-flow
+ );
+ }
+}