Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(526)

Side by Side Diff: packages/stack_trace/lib/src/chain.dart

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « packages/stack_trace/analysis_options.yaml ('k') | packages/stack_trace/lib/src/frame.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library stack_trace.chain;
6
7 import 'dart:async'; 5 import 'dart:async';
8 import 'dart:collection';
9 import 'dart:math' as math; 6 import 'dart:math' as math;
10 7
11 import 'frame.dart'; 8 import 'frame.dart';
9 import 'lazy_chain.dart';
12 import 'stack_zone_specification.dart'; 10 import 'stack_zone_specification.dart';
13 import 'trace.dart'; 11 import 'trace.dart';
14 import 'utils.dart'; 12 import 'utils.dart';
15 13
16 /// A function that handles errors in the zone wrapped by [Chain.capture]. 14 /// A function that handles errors in the zone wrapped by [Chain.capture].
15 @Deprecated("Will be removed in stack_trace 2.0.0.")
17 typedef void ChainHandler(error, Chain chain); 16 typedef void ChainHandler(error, Chain chain);
18 17
18 /// An opaque key used to track the current [StackZoneSpecification].
19 final _specKey = new Object();
20
19 /// A chain of stack traces. 21 /// A chain of stack traces.
20 /// 22 ///
21 /// A stack chain is a collection of one or more stack traces that collectively 23 /// A stack chain is a collection of one or more stack traces that collectively
22 /// represent the path from [main] through nested function calls to a particular 24 /// represent the path from [main] through nested function calls to a particular
23 /// code location, usually where an error was thrown. Multiple stack traces are 25 /// code location, usually where an error was thrown. Multiple stack traces are
24 /// necessary when using asynchronous functions, since the program's stack is 26 /// necessary when using asynchronous functions, since the program's stack is
25 /// reset before each asynchronous callback is run. 27 /// reset before each asynchronous callback is run.
26 /// 28 ///
27 /// Stack chains can be automatically tracked using [Chain.capture]. This sets 29 /// Stack chains can be automatically tracked using [Chain.capture]. This sets
28 /// up a new [Zone] in which the current stack chain is tracked and can be 30 /// up a new [Zone] in which the current stack chain is tracked and can be
29 /// accessed using [new Chain.current]. Any errors that would be top-leveled in 31 /// accessed using [new Chain.current]. Any errors that would be top-leveled in
30 /// the zone can be handled, along with their associated chains, with the 32 /// the zone can be handled, along with their associated chains, with the
31 /// `onError` callback. For example: 33 /// `onError` callback. For example:
32 /// 34 ///
33 /// Chain.capture(() { 35 /// Chain.capture(() {
34 /// // ... 36 /// // ...
35 /// }, onError: (error, stackChain) { 37 /// }, onError: (error, stackChain) {
36 /// print("Caught error $error\n" 38 /// print("Caught error $error\n"
37 /// "$stackChain"); 39 /// "$stackChain");
38 /// }); 40 /// });
39 class Chain implements StackTrace { 41 class Chain implements StackTrace {
40
41 /// The stack traces that make up this chain. 42 /// The stack traces that make up this chain.
42 /// 43 ///
43 /// Like the frames in a stack trace, the traces are ordered from most local 44 /// Like the frames in a stack trace, the traces are ordered from most local
44 /// to least local. The first one is the trace where the actual exception was 45 /// to least local. The first one is the trace where the actual exception was
45 /// raised, the second one is where that callback was scheduled, and so on. 46 /// raised, the second one is where that callback was scheduled, and so on.
46 final List<Trace> traces; 47 final List<Trace> traces;
47 48
48 /// The [StackZoneSpecification] for the current zone. 49 /// The [StackZoneSpecification] for the current zone.
49 static StackZoneSpecification get _currentSpec => 50 static StackZoneSpecification get _currentSpec => Zone.current[_specKey];
50 Zone.current[#stack_trace.stack_zone.spec];
51 51
52 /// Runs [callback] in a [Zone] in which the current stack chain is tracked 52 /// If [when] is `true`, runs [callback] in a [Zone] in which the current
53 /// and automatically associated with (most) errors. 53 /// stack chain is tracked and automatically associated with (most) errors.
54 ///
55 /// If [when] is `false`, this does not track stack chains. Instead, it's
56 /// identical to [runZoned], except that it wraps any errors in [new
57 /// Chain.forTrace]—which will only wrap the trace unless there's a different
58 /// [Chain.capture] active. This makes it easy for the caller to only capture
59 /// stack chains in debug mode or during development.
54 /// 60 ///
55 /// If [onError] is passed, any error in the zone that would otherwise go 61 /// If [onError] is passed, any error in the zone that would otherwise go
56 /// unhandled is passed to it, along with the [Chain] associated with that 62 /// unhandled is passed to it, along with the [Chain] associated with that
57 /// error. Note that if [callback] produces multiple unhandled errors, 63 /// error. Note that if [callback] produces multiple unhandled errors,
58 /// [onError] may be called more than once. If [onError] isn't passed, the 64 /// [onError] may be called more than once. If [onError] isn't passed, the
59 /// parent Zone's `unhandledErrorHandler` will be called with the error and 65 /// parent Zone's `unhandledErrorHandler` will be called with the error and
60 /// its chain. 66 /// its chain.
61 /// 67 ///
62 /// Note that even if [onError] isn't passed, this zone will still be an error 68 /// Note that even if [onError] isn't passed, this zone will still be an error
63 /// zone. This means that any errors that would cross the zone boundary are 69 /// zone. This means that any errors that would cross the zone boundary are
64 /// considered unhandled. 70 /// considered unhandled.
65 /// 71 ///
66 /// If [callback] returns a value, it will be returned by [capture] as well. 72 /// If [callback] returns a value, it will be returned by [capture] as well.
67 static capture(callback(), {ChainHandler onError}) { 73 static T capture<T>(T callback(),
74 {void onError(error, Chain chain), bool when: true}) {
75 if (!when) {
76 var newOnError;
77 if (onError != null) {
78 newOnError = (error, stackTrace) {
79 onError(
80 error,
81 stackTrace == null
82 ? new Chain.current()
83 : new Chain.forTrace(stackTrace));
84 };
85 }
86
87 return runZoned(callback, onError: newOnError);
88 }
89
68 var spec = new StackZoneSpecification(onError); 90 var spec = new StackZoneSpecification(onError);
69 return runZoned(() { 91 return runZoned(() {
70 try { 92 try {
71 return callback(); 93 return callback();
72 } catch (error, stackTrace) { 94 } catch (error, stackTrace) {
73 // TODO(nweiz): Don't special-case this when issue 19566 is fixed. 95 // TODO(nweiz): Don't special-case this when issue 19566 is fixed.
74 return Zone.current.handleUncaughtError(error, stackTrace); 96 return Zone.current.handleUncaughtError(error, stackTrace);
75 } 97 }
76 }, zoneSpecification: spec.toSpec(), zoneValues: { 98 },
77 #stack_trace.stack_zone.spec: spec 99 zoneSpecification: spec.toSpec(),
78 }); 100 zoneValues: {_specKey: spec, StackZoneSpecification.disableKey: false});
101 }
102
103 /// If [when] is `true` and this is called within a [Chain.capture] zone, runs
104 /// [callback] in a [Zone] in which chain capturing is disabled.
105 ///
106 /// If [callback] returns a value, it will be returned by [disable] as well.
107 static/*=T*/ disable/*<T>*/(/*=T*/ callback(), {bool when: true}) {
108 var zoneValues =
109 when ? {_specKey: null, StackZoneSpecification.disableKey: true} : null;
110
111 return runZoned(callback, zoneValues: zoneValues);
79 } 112 }
80 113
81 /// Returns [futureOrStream] unmodified. 114 /// Returns [futureOrStream] unmodified.
82 /// 115 ///
83 /// Prior to Dart 1.7, this was necessary to ensure that stack traces for 116 /// Prior to Dart 1.7, this was necessary to ensure that stack traces for
84 /// exceptions reported with [Completer.completeError] and 117 /// exceptions reported with [Completer.completeError] and
85 /// [StreamController.addError] were tracked correctly. 118 /// [StreamController.addError] were tracked correctly.
86 @Deprecated("Chain.track is not necessary in Dart 1.7+.") 119 @Deprecated("Chain.track is not necessary in Dart 1.7+.")
87 static track(futureOrStream) => futureOrStream; 120 static track(futureOrStream) => futureOrStream;
88 121
89 /// Returns the current stack chain. 122 /// Returns the current stack chain.
90 /// 123 ///
91 /// By default, the first frame of the first trace will be the line where 124 /// By default, the first frame of the first trace will be the line where
92 /// [Chain.current] is called. If [level] is passed, the first trace will 125 /// [Chain.current] is called. If [level] is passed, the first trace will
93 /// start that many frames up instead. 126 /// start that many frames up instead.
94 /// 127 ///
95 /// If this is called outside of a [capture] zone, it just returns a 128 /// If this is called outside of a [capture] zone, it just returns a
96 /// single-trace chain. 129 /// single-trace chain.
97 factory Chain.current([int level=0]) { 130 factory Chain.current([int level = 0]) {
98 if (_currentSpec != null) return _currentSpec.currentChain(level + 1); 131 if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
99 return new Chain([new Trace.current(level + 1)]); 132
133 var chain = new Chain.forTrace(StackTrace.current);
134 return new LazyChain(() {
135 // JS includes a frame for the call to StackTrace.current, but the VM
136 // doesn't, so we skip an extra frame in a JS context.
137 var first = new Trace(
138 chain.traces.first.frames.skip(level + (inJS ? 2 : 1)),
139 original: chain.traces.first.original.toString());
140 return new Chain([first]..addAll(chain.traces.skip(1)));
141 });
100 } 142 }
101 143
102 /// Returns the stack chain associated with [trace]. 144 /// Returns the stack chain associated with [trace].
103 /// 145 ///
104 /// The first stack trace in the returned chain will always be [trace] 146 /// The first stack trace in the returned chain will always be [trace]
105 /// (converted to a [Trace] if necessary). If there is no chain associated 147 /// (converted to a [Trace] if necessary). If there is no chain associated
106 /// with [trace] or if this is called outside of a [capture] zone, this just 148 /// with [trace] or if this is called outside of a [capture] zone, this just
107 /// returns a single-trace chain containing [trace]. 149 /// returns a single-trace chain containing [trace].
108 /// 150 ///
109 /// If [trace] is already a [Chain], it will be returned as-is. 151 /// If [trace] is already a [Chain], it will be returned as-is.
110 factory Chain.forTrace(StackTrace trace) { 152 factory Chain.forTrace(StackTrace trace) {
111 if (trace is Chain) return trace; 153 if (trace is Chain) return trace;
112 if (_currentSpec == null) return new Chain([new Trace.from(trace)]); 154 if (_currentSpec != null) return _currentSpec.chainFor(trace);
113 return _currentSpec.chainFor(trace); 155 return new LazyChain(() => new Chain.parse(trace.toString()));
114 } 156 }
115 157
116 /// Parses a string representation of a stack chain. 158 /// Parses a string representation of a stack chain.
117 /// 159 ///
118 /// If [chain] is the output of a call to [Chain.toString], it will be parsed 160 /// If [chain] is the output of a call to [Chain.toString], it will be parsed
119 /// as a full stack chain. Otherwise, it will be parsed as in [Trace.parse] 161 /// as a full stack chain. Otherwise, it will be parsed as in [Trace.parse]
120 /// and returned as a single-trace chain. 162 /// and returned as a single-trace chain.
121 factory Chain.parse(String chain) { 163 factory Chain.parse(String chain) {
122 if (chain.isEmpty) return new Chain([]); 164 if (chain.isEmpty) return new Chain([]);
165 if (chain.contains(vmChainGap)) {
166 return new Chain(
167 chain.split(vmChainGap).map((trace) => new Trace.parseVM(trace)));
168 }
123 if (!chain.contains(chainGap)) return new Chain([new Trace.parse(chain)]); 169 if (!chain.contains(chainGap)) return new Chain([new Trace.parse(chain)]);
124 170
125 return new Chain( 171 return new Chain(
126 chain.split(chainGap).map((trace) => new Trace.parseFriendly(trace))); 172 chain.split(chainGap).map((trace) => new Trace.parseFriendly(trace)));
127 } 173 }
128 174
129 /// Returns a new [Chain] comprised of [traces]. 175 /// Returns a new [Chain] comprised of [traces].
130 Chain(Iterable<Trace> traces) 176 Chain(Iterable<Trace> traces) : traces = new List<Trace>.unmodifiable(traces);
131 : traces = new UnmodifiableListView<Trace>(traces.toList());
132 177
133 /// Returns a terser version of [this]. 178 /// Returns a terser version of [this].
134 /// 179 ///
135 /// This calls [Trace.terse] on every trace in [traces], and discards any 180 /// This calls [Trace.terse] on every trace in [traces], and discards any
136 /// trace that contain only internal frames. 181 /// trace that contain only internal frames.
182 ///
183 /// This won't do anything with a raw JavaScript trace, since there's no way
184 /// to determine which frames come from which Dart libraries. However, the
185 /// [`source_map_stack_trace`][source_map_stack_trace] package can be used to
186 /// convert JavaScript traces into Dart-style traces.
187 ///
188 /// [source_map_stack_trace]: https://pub.dartlang.org/packages/source_map_sta ck_trace
137 Chain get terse => foldFrames((_) => false, terse: true); 189 Chain get terse => foldFrames((_) => false, terse: true);
138 190
139 /// Returns a new [Chain] based on [this] where multiple stack frames matching 191 /// Returns a new [Chain] based on [this] where multiple stack frames matching
140 /// [predicate] are folded together. 192 /// [predicate] are folded together.
141 /// 193 ///
142 /// This means that whenever there are multiple frames in a row that match 194 /// This means that whenever there are multiple frames in a row that match
143 /// [predicate], only the last one is kept. In addition, traces that are 195 /// [predicate], only the last one is kept. In addition, traces that are
144 /// composed entirely of frames matching [predicate] are omitted. 196 /// composed entirely of frames matching [predicate] are omitted.
145 /// 197 ///
146 /// This is useful for limiting the amount of library code that appears in a 198 /// This is useful for limiting the amount of library code that appears in a
147 /// stack trace by only showing user code and code that's called by user code. 199 /// stack trace by only showing user code and code that's called by user code.
148 /// 200 ///
149 /// If [terse] is true, this will also fold together frames from the core 201 /// If [terse] is true, this will also fold together frames from the core
150 /// library or from this package, and simplify core library frames as in 202 /// library or from this package, and simplify core library frames as in
151 /// [Trace.terse]. 203 /// [Trace.terse].
152 Chain foldFrames(bool predicate(Frame frame), {bool terse: false}) { 204 Chain foldFrames(bool predicate(Frame frame), {bool terse: false}) {
153 var foldedTraces = traces.map( 205 var foldedTraces =
154 (trace) => trace.foldFrames(predicate, terse: terse)); 206 traces.map((trace) => trace.foldFrames(predicate, terse: terse));
155 var nonEmptyTraces = foldedTraces.where((trace) { 207 var nonEmptyTraces = foldedTraces.where((trace) {
156 // Ignore traces that contain only folded frames. 208 // Ignore traces that contain only folded frames.
157 if (trace.frames.length > 1) return true; 209 if (trace.frames.length > 1) return true;
210 if (trace.frames.isEmpty) return false;
158 211
159 // In terse mode, the trace may have removed an outer folded frame, 212 // In terse mode, the trace may have removed an outer folded frame,
160 // leaving a single non-folded frame. We can detect a folded frame because 213 // leaving a single non-folded frame. We can detect a folded frame because
161 // it has no line information. 214 // it has no line information.
162 if (!terse) return false; 215 if (!terse) return false;
163 return trace.frames.single.line != null; 216 return trace.frames.single.line != null;
164 }); 217 });
165 218
166 // If all the traces contain only internal processing, preserve the last 219 // If all the traces contain only internal processing, preserve the last
167 // (top-most) one so that the chain isn't empty. 220 // (top-most) one so that the chain isn't empty.
168 if (nonEmptyTraces.isEmpty && foldedTraces.isNotEmpty) { 221 if (nonEmptyTraces.isEmpty && foldedTraces.isNotEmpty) {
169 return new Chain([foldedTraces.last]); 222 return new Chain([foldedTraces.last]);
170 } 223 }
171 224
172 return new Chain(nonEmptyTraces); 225 return new Chain(nonEmptyTraces);
173 } 226 }
174 227
175 /// Converts [this] to a [Trace]. 228 /// Converts [this] to a [Trace].
176 /// 229 ///
177 /// The trace version of a chain is just the concatenation of all the traces 230 /// The trace version of a chain is just the concatenation of all the traces
178 /// in the chain. 231 /// in the chain.
179 Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames))); 232 Trace toTrace() => new Trace(traces.expand((trace) => trace.frames));
180 233
181 String toString() { 234 String toString() {
182 // Figure out the longest path so we know how much to pad. 235 // Figure out the longest path so we know how much to pad.
183 var longest = traces.map((trace) { 236 var longest = traces.map((trace) {
184 return trace.frames.map((frame) => frame.location.length) 237 return trace.frames
238 .map((frame) => frame.location.length)
185 .fold(0, math.max); 239 .fold(0, math.max);
186 }).fold(0, math.max); 240 }).fold(0, math.max);
187 241
188 // Don't call out to [Trace.toString] here because that doesn't ensure that 242 // Don't call out to [Trace.toString] here because that doesn't ensure that
189 // padding is consistent across all traces. 243 // padding is consistent across all traces.
190 return traces.map((trace) { 244 return traces.map((trace) {
191 return trace.frames.map((frame) { 245 return trace.frames.map((frame) {
192 return '${padRight(frame.location, longest)} ${frame.member}\n'; 246 return '${frame.location.padRight(longest)} ${frame.member}\n';
193 }).join(); 247 }).join();
194 }).join(chainGap); 248 }).join(chainGap);
195 } 249 }
196 } 250 }
OLDNEW
« no previous file with comments | « packages/stack_trace/analysis_options.yaml ('k') | packages/stack_trace/lib/src/frame.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698