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

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

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 | « source_span/pubspec.yaml ('k') | 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library stack_trace.chain;
6
7 import 'dart:async';
8 import 'dart:collection';
9 import 'dart:math' as math;
10
11 import 'frame.dart';
12 import 'stack_zone_specification.dart';
13 import 'trace.dart';
14 import 'utils.dart';
15
16 /// A function that handles errors in the zone wrapped by [Chain.capture].
17 typedef void ChainHandler(error, Chain chain);
18
19 /// The line used in the string representation of stack chains to represent
20 /// the gap between traces.
21 const _gap = '===== asynchronous gap ===========================\n';
22
23 /// A chain of stack traces.
24 ///
25 /// A stack chain is a collection of one or more stack traces that collectively
26 /// represent the path from [main] through nested function calls to a particular
27 /// code location, usually where an error was thrown. Multiple stack traces are
28 /// necessary when using asynchronous functions, since the program's stack is
29 /// reset before each asynchronous callback is run.
30 ///
31 /// Stack chains can be automatically tracked using [Chain.capture]. This sets
32 /// up a new [Zone] in which the current stack chain is tracked and can be
33 /// accessed using [new Chain.current]. Any errors that would be top-leveled in
34 /// the zone can be handled, along with their associated chains, with the
35 /// `onError` callback. For example:
36 ///
37 /// Chain.capture(() {
38 /// // ...
39 /// }, onError: (error, stackChain) {
40 /// print("Caught error $error\n"
41 /// "$stackChain");
42 /// });
43 class Chain implements StackTrace {
44
45 /// The stack traces that make up this chain.
46 ///
47 /// Like the frames in a stack trace, the traces are ordered from most local
48 /// to least local. The first one is the trace where the actual exception was
49 /// raised, the second one is where that callback was scheduled, and so on.
50 final List<Trace> traces;
51
52 /// The [StackZoneSpecification] for the current zone.
53 static StackZoneSpecification get _currentSpec =>
54 Zone.current[#stack_trace.stack_zone.spec];
55
56 /// Runs [callback] in a [Zone] in which the current stack chain is tracked
57 /// and automatically associated with (most) errors.
58 ///
59 /// If [onError] is passed, any error in the zone that would otherwise go
60 /// unhandled is passed to it, along with the [Chain] associated with that
61 /// error. Note that if [callback] produces multiple unhandled errors,
62 /// [onError] may be called more than once. If [onError] isn't passed, the
63 /// parent Zone's `unhandledErrorHandler` will be called with the error and
64 /// its chain.
65 ///
66 /// Note that even if [onError] isn't passed, this zone will still be an error
67 /// zone. This means that any errors that would cross the zone boundary are
68 /// considered unhandled.
69 ///
70 /// If [callback] returns a value, it will be returned by [capture] as well.
71 static capture(callback(), {ChainHandler onError}) {
72 var spec = new StackZoneSpecification(onError);
73 return runZoned(() {
74 try {
75 return callback();
76 } catch (error, stackTrace) {
77 // TODO(nweiz): Don't special-case this when issue 19566 is fixed.
78 return Zone.current.handleUncaughtError(error, stackTrace);
79 }
80 }, zoneSpecification: spec.toSpec(), zoneValues: {
81 #stack_trace.stack_zone.spec: spec
82 });
83 }
84
85 /// Returns [futureOrStream] unmodified.
86 ///
87 /// Prior to Dart 1.7, this was necessary to ensure that stack traces for
88 /// exceptions reported with [Completer.completeError] and
89 /// [StreamController.addError] were tracked correctly.
90 @Deprecated("Chain.track is not necessary in Dart 1.7+.")
91 static track(futureOrStream) => futureOrStream;
92
93 /// Returns the current stack chain.
94 ///
95 /// By default, the first frame of the first trace will be the line where
96 /// [Chain.current] is called. If [level] is passed, the first trace will
97 /// start that many frames up instead.
98 ///
99 /// If this is called outside of a [capture] zone, it just returns a
100 /// single-trace chain.
101 factory Chain.current([int level=0]) {
102 if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
103 return new Chain([new Trace.current(level + 1)]);
104 }
105
106 /// Returns the stack chain associated with [trace].
107 ///
108 /// The first stack trace in the returned chain will always be [trace]
109 /// (converted to a [Trace] if necessary). If there is no chain associated
110 /// with [trace] or if this is called outside of a [capture] zone, this just
111 /// returns a single-trace chain containing [trace].
112 ///
113 /// If [trace] is already a [Chain], it will be returned as-is.
114 factory Chain.forTrace(StackTrace trace) {
115 if (trace is Chain) return trace;
116 if (_currentSpec == null) return new Chain([new Trace.from(trace)]);
117 return _currentSpec.chainFor(trace);
118 }
119
120 /// Parses a string representation of a stack chain.
121 ///
122 /// Specifically, this parses the output of [Chain.toString].
123 factory Chain.parse(String chain) {
124 if (chain.isEmpty) return new Chain([]);
125 return new Chain(
126 chain.split(_gap).map((trace) => new Trace.parseFriendly(trace)));
127 }
128
129 /// Returns a new [Chain] comprised of [traces].
130 Chain(Iterable<Trace> traces)
131 : traces = new UnmodifiableListView<Trace>(traces.toList());
132
133 /// Returns a terser version of [this].
134 ///
135 /// This calls [Trace.terse] on every trace in [traces], and discards any
136 /// trace that contain only internal frames.
137 Chain get terse => foldFrames((_) => false, terse: true);
138
139 /// Returns a new [Chain] based on [this] where multiple stack frames matching
140 /// [predicate] are folded together.
141 ///
142 /// 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
144 /// composed entirely of frames matching [predicate] are omitted.
145 ///
146 /// 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.
148 ///
149 /// 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
151 /// [Trace.terse].
152 Chain foldFrames(bool predicate(Frame frame), {bool terse: false}) {
153 var foldedTraces = traces.map(
154 (trace) => trace.foldFrames(predicate, terse: terse));
155 var nonEmptyTraces = foldedTraces.where((trace) {
156 // Ignore traces that contain only folded frames.
157 if (trace.frames.length > 1) return true;
158
159 // 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
161 // it has no line information.
162 if (!terse) return false;
163 return trace.frames.single.line != null;
164 });
165
166 // If all the traces contain only internal processing, preserve the last
167 // (top-most) one so that the chain isn't empty.
168 if (nonEmptyTraces.isEmpty && foldedTraces.isNotEmpty) {
169 return new Chain([foldedTraces.last]);
170 }
171
172 return new Chain(nonEmptyTraces);
173 }
174
175 /// Converts [this] to a [Trace].
176 ///
177 /// The trace version of a chain is just the concatenation of all the traces
178 /// in the chain.
179 Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames)));
180
181 String toString() {
182 // Figure out the longest path so we know how much to pad.
183 var longest = traces.map((trace) {
184 return trace.frames.map((frame) => frame.location.length)
185 .fold(0, math.max);
186 }).fold(0, math.max);
187
188 // Don't call out to [Trace.toString] here because that doesn't ensure that
189 // padding is consistent across all traces.
190 return traces.map((trace) {
191 return trace.frames.map((frame) {
192 return '${padRight(frame.location, longest)} ${frame.member}\n';
193 }).join();
194 }).join(_gap);
195 }
196 }
OLDNEW
« no previous file with comments | « source_span/pubspec.yaml ('k') | stack_trace/lib/src/frame.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698