Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
19e624cc
Commit
19e624cc
authored
Apr 05, 2016
by
Yegor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[driver] give the timeline data some structure
Fixes
https://github.com/flutter/flutter/issues/2713
parent
6ea7ab89
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
220 additions
and
41 deletions
+220
-41
scroll_perf_test.dart
...nchmarks/complex_layout/test_driver/scroll_perf_test.dart
+4
-5
scroll_perf_test.dart
examples/stocks/test_driver/scroll_perf_test.dart
+2
-3
flutter_driver.dart
packages/flutter_driver/lib/flutter_driver.dart
+4
-0
driver.dart
packages/flutter_driver/lib/src/driver.dart
+8
-9
timeline.dart
packages/flutter_driver/lib/src/timeline.dart
+119
-0
timeline_summary.dart
packages/flutter_driver/lib/src/timeline_summary.dart
+16
-18
flutter_driver_test.dart
packages/flutter_driver/test/flutter_driver_test.dart
+8
-3
timeline_summary_test.dart
packages/flutter_driver/test/src/timeline_summary_test.dart
+3
-3
timeline_test.dart
packages/flutter_driver/test/src/timeline_test.dart
+56
-0
No files found.
dev/benchmarks/complex_layout/test_driver/scroll_perf_test.dart
View file @
19e624cc
...
@@ -3,8 +3,6 @@
...
@@ -3,8 +3,6 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'package:flutter_driver/flutter_driver.dart'
;
import
'package:flutter_driver/flutter_driver.dart'
;
import
'package:test/test.dart'
;
import
'package:test/test.dart'
;
...
@@ -22,7 +20,7 @@ void main() {
...
@@ -22,7 +20,7 @@ void main() {
});
});
test
(
'measure'
,
()
async
{
test
(
'measure'
,
()
async
{
Map
<
String
,
dynamic
>
profileJson
=
await
driver
.
traceAction
(()
async
{
Timeline
timeline
=
await
driver
.
traceAction
(()
async
{
// Find the scrollable stock list
// Find the scrollable stock list
ObjectRef
stockList
=
await
driver
.
findByValueKey
(
'main-scroll'
);
ObjectRef
stockList
=
await
driver
.
findByValueKey
(
'main-scroll'
);
expect
(
stockList
,
isNotNull
);
expect
(
stockList
,
isNotNull
);
...
@@ -40,8 +38,9 @@ void main() {
...
@@ -40,8 +38,9 @@ void main() {
}
}
});
});
expect
(
profileJson
,
isNotNull
);
TimelineSummary
summary
=
new
TimelineSummary
.
summarize
(
timeline
);
await
new
File
(
"build/profile.json"
).
writeAsString
(
JSON
.
encode
(
profileJson
));
summary
.
writeSummaryToFile
(
'complex_layout_scroll_perf'
,
pretty:
true
);
summary
.
writeTimelineToFile
(
'complex_layout_scroll_perf'
,
pretty:
true
);
});
});
});
});
}
}
examples/stocks/test_driver/scroll_perf_test.dart
View file @
19e624cc
...
@@ -21,7 +21,7 @@ void main() {
...
@@ -21,7 +21,7 @@ void main() {
});
});
test
(
'measure'
,
()
async
{
test
(
'measure'
,
()
async
{
Map
<
String
,
dynamic
>
timeline
=
await
driver
.
traceAction
(()
async
{
Timeline
timeline
=
await
driver
.
traceAction
(()
async
{
// Find the scrollable stock list
// Find the scrollable stock list
ObjectRef
stockList
=
await
driver
.
findByValueKey
(
'stock-list'
);
ObjectRef
stockList
=
await
driver
.
findByValueKey
(
'stock-list'
);
expect
(
stockList
,
isNotNull
);
expect
(
stockList
,
isNotNull
);
...
@@ -39,8 +39,7 @@ void main() {
...
@@ -39,8 +39,7 @@ void main() {
}
}
});
});
expect
(
timeline
,
isNotNull
);
TimelineSummary
summary
=
new
TimelineSummary
.
summarize
(
timeline
);
TimelineSummary
summary
=
summarizeTimeline
(
timeline
);
summary
.
writeSummaryToFile
(
'stocks_scroll_perf'
,
pretty:
true
);
summary
.
writeSummaryToFile
(
'stocks_scroll_perf'
,
pretty:
true
);
summary
.
writeTimelineToFile
(
'stocks_scroll_perf'
,
pretty:
true
);
summary
.
writeTimelineToFile
(
'stocks_scroll_perf'
,
pretty:
true
);
});
});
...
...
packages/flutter_driver/lib/flutter_driver.dart
View file @
19e624cc
...
@@ -41,3 +41,7 @@ export 'src/timeline_summary.dart' show
...
@@ -41,3 +41,7 @@ export 'src/timeline_summary.dart' show
summarizeTimeline
,
summarizeTimeline
,
EventTrace
,
EventTrace
,
TimelineSummary
;
TimelineSummary
;
export
'src/timeline.dart'
show
Timeline
,
TimelineEvent
;
packages/flutter_driver/lib/src/driver.dart
View file @
19e624cc
...
@@ -15,6 +15,7 @@ import 'health.dart';
...
@@ -15,6 +15,7 @@ import 'health.dart';
import
'matcher_util.dart'
;
import
'matcher_util.dart'
;
import
'message.dart'
;
import
'message.dart'
;
import
'retry.dart'
;
import
'retry.dart'
;
import
'timeline.dart'
;
final
Logger
_log
=
new
Logger
(
'FlutterDriver'
);
final
Logger
_log
=
new
Logger
(
'FlutterDriver'
);
...
@@ -229,16 +230,14 @@ class FlutterDriver {
...
@@ -229,16 +230,14 @@ class FlutterDriver {
}
}
}
}
/// Stops recording performance traces and downloads the trace profile.
/// Stops recording performance traces and downloads the timeline.
// TODO(yjbanov): return structured data rather than raw JSON once we have a
Future
<
Timeline
>
stopTracingAndDownloadTimeline
()
async
{
// stable protocol to talk to.
Future
<
Map
<
String
,
dynamic
>>
stopTracingAndDownloadProfile
()
async
{
try
{
try
{
await
_peer
.
sendRequest
(
_kSetVMTimelineFlagsMethod
,
{
'recordedStreams'
:
'[]'
});
await
_peer
.
sendRequest
(
_kSetVMTimelineFlagsMethod
,
{
'recordedStreams'
:
'[]'
});
return
_peer
.
sendRequest
(
_kGetVMTimelineMethod
);
return
new
Timeline
.
fromJson
(
await
_peer
.
sendRequest
(
_kGetVMTimelineMethod
)
);
}
catch
(
error
,
stackTrace
)
{
}
catch
(
error
,
stackTrace
)
{
throw
new
DriverError
(
throw
new
DriverError
(
'Failed to st
art
tracing due to remote error'
,
'Failed to st
op
tracing due to remote error'
,
error
,
error
,
stackTrace
stackTrace
);
);
...
@@ -251,11 +250,11 @@ class FlutterDriver {
...
@@ -251,11 +250,11 @@ class FlutterDriver {
/// the trace.
/// the trace.
///
///
/// This is merely a convenience wrapper on top of [startTracing] and
/// This is merely a convenience wrapper on top of [startTracing] and
/// [stopTracingAndDownload
Profil
e].
/// [stopTracingAndDownload
Timelin
e].
Future
<
Map
<
String
,
dynamic
>
>
traceAction
(
Future
<
dynamic
>
action
())
async
{
Future
<
Timeline
>
traceAction
(
Future
<
dynamic
>
action
())
async
{
await
startTracing
();
await
startTracing
();
await
action
();
await
action
();
return
stopTracingAndDownload
Profil
e
();
return
stopTracingAndDownload
Timelin
e
();
}
}
/// Calls the [evaluator] repeatedly until the result of the evaluation
/// Calls the [evaluator] repeatedly until the result of the evaluation
...
...
packages/flutter_driver/lib/src/timeline.dart
0 → 100644
View file @
19e624cc
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Timeline data recorded by the Flutter runtime.
///
/// The data is in the `chrome://tracing` format. It can be saved to a file
/// and loaded in Chrome for visual inspection.
///
/// See https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
class
Timeline
{
factory
Timeline
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
return
new
Timeline
.
_
(
json
,
_parseEvents
(
json
));
}
Timeline
.
_
(
this
.
json
,
this
.
events
);
/// The original timeline JSON.
final
Map
<
String
,
dynamic
>
json
;
/// List of all timeline events.
final
List
<
TimelineEvent
>
events
;
}
/// A single timeline event.
class
TimelineEvent
{
factory
TimelineEvent
(
Map
<
String
,
dynamic
>
json
)
{
return
new
TimelineEvent
.
_
(
json
,
json
[
'name'
],
json
[
'cat'
],
json
[
'ph'
],
json
[
'pid'
],
json
[
'tid'
],
json
[
'dur'
]
!=
null
?
new
Duration
(
microseconds:
json
[
'dur'
])
:
null
,
json
[
'ts'
],
json
[
'tts'
],
json
[
'args'
]
);
}
TimelineEvent
.
_
(
this
.
json
,
this
.
name
,
this
.
category
,
this
.
phase
,
this
.
processId
,
this
.
threadId
,
this
.
duration
,
this
.
timestampMicros
,
this
.
threadTimestampMicros
,
this
.
arguments
);
/// The original event JSON.
final
Map
<
String
,
dynamic
>
json
;
/// The name of the event.
///
/// Corresponds to the "name" field in the JSON event.
final
String
name
;
/// Event category. Events with different names may share the same category.
///
/// Corresponds to the "cat" field in the JSON event.
final
String
category
;
/// For a given long lasting event, denotes the phase of the event, such as
/// "B" for "event began", and "E" for "event ended".
///
/// Corresponds to the "ph" field in the JSON event.
final
String
phase
;
/// ID of process that emitted the event.
///
/// Corresponds to the "pid" field in the JSON event.
final
int
processId
;
/// ID of thread that issues the event.
///
/// Corresponds to the "tid" field in the JSON event.
final
int
threadId
;
/// The duration of the event.
///
/// Note, some events are reported with duration. Others are reported as a
/// pair of begin/end events.
///
/// Corresponds to the "dur" field in the JSON event.
final
Duration
duration
;
/// Time passed since tracing was enabled, in microseconds.
///
/// Corresponds to the "ts" field in the JSON event.
final
int
timestampMicros
;
/// Thread clock time, in microseconds.
///
/// Corresponds to the "tts" field in the JSON event.
final
int
threadTimestampMicros
;
/// Arbitrary data attached to the event.
///
/// Corresponds to the "args" field in the JSON event.
final
Map
<
String
,
dynamic
>
arguments
;
}
List
<
TimelineEvent
>
_parseEvents
(
Map
<
String
,
dynamic
>
json
)
{
List
<
Map
<
String
,
dynamic
>>
jsonEvents
=
json
[
'traceEvents'
];
if
(
jsonEvents
==
null
)
return
null
;
return
jsonEvents
.
map
((
Map
<
String
,
dynamic
>
eventJson
)
=>
new
TimelineEvent
(
eventJson
))
.
toList
();
}
packages/flutter_driver/lib/src/timeline_summary.dart
View file @
19e624cc
...
@@ -9,6 +9,7 @@ import 'package:file/file.dart';
...
@@ -9,6 +9,7 @@ import 'package:file/file.dart';
import
'package:path/path.dart'
as
path
;
import
'package:path/path.dart'
as
path
;
import
'common.dart'
;
import
'common.dart'
;
import
'timeline.dart'
;
const
String
_kDefaultDirectory
=
'build'
;
const
String
_kDefaultDirectory
=
'build'
;
final
JsonEncoder
_prettyEncoder
=
new
JsonEncoder
.
withIndent
(
' '
);
final
JsonEncoder
_prettyEncoder
=
new
JsonEncoder
.
withIndent
(
' '
);
...
@@ -18,14 +19,10 @@ final JsonEncoder _prettyEncoder = new JsonEncoder.withIndent(' ');
...
@@ -18,14 +19,10 @@ final JsonEncoder _prettyEncoder = new JsonEncoder.withIndent(' ');
const
Duration
kBuildBudget
=
const
Duration
(
milliseconds:
8
);
const
Duration
kBuildBudget
=
const
Duration
(
milliseconds:
8
);
/// Extracts statistics from the event loop timeline.
/// Extracts statistics from the event loop timeline.
TimelineSummary
summarizeTimeline
(
Map
<
String
,
dynamic
>
timeline
)
{
return
new
TimelineSummary
(
timeline
);
}
class
TimelineSummary
{
class
TimelineSummary
{
TimelineSummary
(
this
.
_timeline
);
TimelineSummary
.
summarize
(
this
.
_timeline
);
final
Map
<
String
,
dynamic
>
_timeline
;
final
Timeline
_timeline
;
/// Average amount of time spent per frame in the framework building widgets,
/// Average amount of time spent per frame in the framework building widgets,
/// updating layout, painting and compositing.
/// updating layout, painting and compositing.
...
@@ -68,7 +65,7 @@ class TimelineSummary {
...
@@ -68,7 +65,7 @@ class TimelineSummary {
{
String
destinationDirectory:
_kDefaultDirectory
,
bool
pretty:
false
})
async
{
{
String
destinationDirectory:
_kDefaultDirectory
,
bool
pretty:
false
})
async
{
await
fs
.
directory
(
destinationDirectory
).
create
(
recursive:
true
);
await
fs
.
directory
(
destinationDirectory
).
create
(
recursive:
true
);
File
file
=
fs
.
file
(
path
.
join
(
destinationDirectory
,
'
$traceName
.timeline.json'
));
File
file
=
fs
.
file
(
path
.
join
(
destinationDirectory
,
'
$traceName
.timeline.json'
));
await
file
.
writeAsString
(
_encodeJson
(
_timeline
,
pretty
));
await
file
.
writeAsString
(
_encodeJson
(
_timeline
.
json
,
pretty
));
}
}
/// Writes [summaryJson] to a file.
/// Writes [summaryJson] to a file.
...
@@ -79,17 +76,15 @@ class TimelineSummary {
...
@@ -79,17 +76,15 @@ class TimelineSummary {
await
file
.
writeAsString
(
_encodeJson
(
summaryJson
,
pretty
));
await
file
.
writeAsString
(
_encodeJson
(
summaryJson
,
pretty
));
}
}
String
_encodeJson
(
dynamic
json
,
bool
pretty
)
{
String
_encodeJson
(
Map
<
String
,
dynamic
>
json
,
bool
pretty
)
{
return
pretty
return
pretty
?
_prettyEncoder
.
convert
(
json
)
?
_prettyEncoder
.
convert
(
json
)
:
JSON
.
encode
(
json
);
:
JSON
.
encode
(
json
);
}
}
List
<
Map
<
String
,
dynamic
>>
get
_traceEvents
=>
_timeline
[
'traceEvents'
];
List
<
TimelineEvent
>
_extractNamedEvents
(
String
name
)
{
return
_timeline
.
events
List
<
Map
<
String
,
dynamic
>>
_extractNamedEvents
(
String
name
)
{
.
where
((
TimelineEvent
event
)
=>
event
.
name
==
name
)
return
_traceEvents
.
where
((
Map
<
String
,
dynamic
>
event
)
=>
event
[
'name'
]
==
name
)
.
toList
();
.
toList
();
}
}
...
@@ -98,13 +93,16 @@ class TimelineSummary {
...
@@ -98,13 +93,16 @@ class TimelineSummary {
List
<
TimedEvent
>
result
=
<
TimedEvent
>[];
List
<
TimedEvent
>
result
=
<
TimedEvent
>[];
// Timeline does not guarantee that the first event is the "begin" event.
// Timeline does not guarantee that the first event is the "begin" event.
Iterator
<
Map
<
String
,
dynamic
>
>
events
=
_extractNamedEvents
(
name
)
Iterator
<
TimelineEvent
>
events
=
_extractNamedEvents
(
name
)
.
skipWhile
((
Map
<
String
,
dynamic
>
evt
)
=>
evt
[
'ph'
]
!=
'B'
).
iterator
;
.
skipWhile
((
TimelineEvent
evt
)
=>
evt
.
phase
!=
'B'
).
iterator
;
while
(
events
.
moveNext
())
{
while
(
events
.
moveNext
())
{
Map
<
String
,
dynamic
>
beginEvent
=
events
.
current
;
TimelineEvent
beginEvent
=
events
.
current
;
if
(
events
.
moveNext
())
{
if
(
events
.
moveNext
())
{
Map
<
String
,
dynamic
>
endEvent
=
events
.
current
;
TimelineEvent
endEvent
=
events
.
current
;
result
.
add
(
new
TimedEvent
(
beginEvent
[
'ts'
],
endEvent
[
'ts'
]));
result
.
add
(
new
TimedEvent
(
beginEvent
.
timestampMicros
,
endEvent
.
timestampMicros
));
}
}
}
}
...
...
packages/flutter_driver/test/flutter_driver_test.dart
View file @
19e624cc
...
@@ -8,6 +8,7 @@ import 'package:flutter_driver/src/driver.dart';
...
@@ -8,6 +8,7 @@ import 'package:flutter_driver/src/driver.dart';
import
'package:flutter_driver/src/error.dart'
;
import
'package:flutter_driver/src/error.dart'
;
import
'package:flutter_driver/src/health.dart'
;
import
'package:flutter_driver/src/health.dart'
;
import
'package:flutter_driver/src/message.dart'
;
import
'package:flutter_driver/src/message.dart'
;
import
'package:flutter_driver/src/timeline.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'package:quiver/testing/async.dart'
;
...
@@ -262,18 +263,22 @@ void main() {
...
@@ -262,18 +263,22 @@ void main() {
when
(
mockPeer
.
sendRequest
(
'_getVMTimeline'
)).
thenAnswer
((
_
)
async
{
when
(
mockPeer
.
sendRequest
(
'_getVMTimeline'
)).
thenAnswer
((
_
)
async
{
return
<
String
,
dynamic
>
{
return
<
String
,
dynamic
>
{
'test'
:
'profile'
,
'traceEvents'
:
[
{
'name'
:
'test event'
}
],
};
};
});
});
Map
<
String
,
dynamic
>
profil
e
=
await
driver
.
traceAction
(()
{
Timeline
timelin
e
=
await
driver
.
traceAction
(()
{
actionCalled
=
true
;
actionCalled
=
true
;
});
});
expect
(
actionCalled
,
isTrue
);
expect
(
actionCalled
,
isTrue
);
expect
(
startTracingCalled
,
isTrue
);
expect
(
startTracingCalled
,
isTrue
);
expect
(
stopTracingCalled
,
isTrue
);
expect
(
stopTracingCalled
,
isTrue
);
expect
(
profile
[
'test'
],
'profile
'
);
expect
(
timeline
.
events
.
single
.
name
,
'test event
'
);
});
});
});
});
});
});
...
...
packages/flutter_driver/test/src/timeline_summary_test.dart
View file @
19e624cc
...
@@ -6,15 +6,15 @@ import 'dart:convert' show JSON;
...
@@ -6,15 +6,15 @@ import 'dart:convert' show JSON;
import
'package:test/test.dart'
;
import
'package:test/test.dart'
;
import
'package:flutter_driver/src/common.dart'
;
import
'package:flutter_driver/src/common.dart'
;
import
'package:flutter_driver/
src/timeline_summary
.dart'
;
import
'package:flutter_driver/
flutter_driver
.dart'
;
void
main
(
)
{
void
main
(
)
{
group
(
'TimelineSummary'
,
()
{
group
(
'TimelineSummary'
,
()
{
TimelineSummary
summarize
(
List
<
Map
<
String
,
dynamic
>>
testEvents
)
{
TimelineSummary
summarize
(
List
<
Map
<
String
,
dynamic
>>
testEvents
)
{
return
summarizeTimeline
(<
String
,
dynamic
>{
return
new
TimelineSummary
.
summarize
(
new
Timeline
.
fromJson
(<
String
,
dynamic
>{
'traceEvents'
:
testEvents
,
'traceEvents'
:
testEvents
,
});
})
)
;
}
}
Map
<
String
,
dynamic
>
begin
(
int
timeStamp
)
=>
<
String
,
dynamic
>{
Map
<
String
,
dynamic
>
begin
(
int
timeStamp
)
=>
<
String
,
dynamic
>{
...
...
packages/flutter_driver/test/src/timeline_test.dart
0 → 100644
View file @
19e624cc
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:test/test.dart'
;
import
'package:flutter_driver/src/timeline.dart'
;
void
main
(
)
{
group
(
'Timeline'
,
()
{
test
(
'parses JSON'
,
()
{
Timeline
timeline
=
new
Timeline
.
fromJson
({
'traceEvents'
:
[
{
'name'
:
'test event'
,
'cat'
:
'test category'
,
'ph'
:
'B'
,
'pid'
:
123
,
'tid'
:
234
,
'dur'
:
345
,
'ts'
:
456
,
'tts'
:
567
,
'args'
:
{
'arg1'
:
true
,
}
},
// Tests that we don't choke on missing data
{}
]
});
expect
(
timeline
.
events
,
hasLength
(
2
));
TimelineEvent
e1
=
timeline
.
events
[
0
];
expect
(
e1
.
name
,
'test event'
);
expect
(
e1
.
category
,
'test category'
);
expect
(
e1
.
phase
,
'B'
);
expect
(
e1
.
processId
,
123
);
expect
(
e1
.
threadId
,
234
);
expect
(
e1
.
duration
,
const
Duration
(
microseconds:
345
));
expect
(
e1
.
timestampMicros
,
456
);
expect
(
e1
.
threadTimestampMicros
,
567
);
expect
(
e1
.
arguments
,
{
'arg1'
:
true
});
TimelineEvent
e2
=
timeline
.
events
[
1
];
expect
(
e2
.
name
,
isNull
);
expect
(
e2
.
category
,
isNull
);
expect
(
e2
.
phase
,
isNull
);
expect
(
e2
.
processId
,
isNull
);
expect
(
e2
.
threadId
,
isNull
);
expect
(
e2
.
duration
,
isNull
);
expect
(
e2
.
timestampMicros
,
isNull
);
expect
(
e2
.
threadTimestampMicros
,
isNull
);
expect
(
e2
.
arguments
,
isNull
);
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment