comparison jvmci/com.oracle.jvmci.debug/src/com/oracle/jvmci/debug/internal/DebugScope.java @ 21798:395ac43a8578

moved JVMCI sources from graal/ to jvmci/ directory
author Doug Simon <doug.simon@oracle.com>
date Tue, 09 Jun 2015 00:22:49 +0200
parents graal/com.oracle.jvmci.debug/src/com/oracle/jvmci/debug/internal/DebugScope.java@f5b549811bac
children 90e3fecd4143
comparison
equal deleted inserted replaced
21797:42452d2dfbec 21798:395ac43a8578
1 /*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.jvmci.debug.internal;
24
25 import java.io.*;
26 import java.util.*;
27 import java.util.concurrent.*;
28
29 import com.oracle.jvmci.debug.*;
30
31 public final class DebugScope implements Debug.Scope {
32
33 private final class IndentImpl implements Indent {
34
35 private static final String INDENTATION_INCREMENT = " ";
36
37 final String indent;
38 final IndentImpl parentIndent;
39
40 IndentImpl(IndentImpl parentIndent) {
41 this.parentIndent = parentIndent;
42 this.indent = (parentIndent == null ? "" : parentIndent.indent + INDENTATION_INCREMENT);
43 }
44
45 private void printScopeName(StringBuilder str) {
46 if (logScopeName) {
47 if (parentIndent != null) {
48 parentIndent.printScopeName(str);
49 }
50 str.append(indent).append("[thread:").append(Thread.currentThread().getId()).append("] scope: ").append(getQualifiedName()).append(System.lineSeparator());
51 logScopeName = false;
52 }
53 }
54
55 public void log(int logLevel, String msg, Object... args) {
56 if (isLogEnabled(logLevel)) {
57 StringBuilder str = new StringBuilder();
58 printScopeName(str);
59 str.append(indent);
60 String result = args.length == 0 ? msg : String.format(msg, args);
61 String lineSep = System.lineSeparator();
62 str.append(result.replace(lineSep, lineSep.concat(indent)));
63 str.append(lineSep);
64 output.append(str);
65 lastUsedIndent = this;
66 }
67 }
68
69 IndentImpl indent() {
70 lastUsedIndent = new IndentImpl(this);
71 return lastUsedIndent;
72 }
73
74 @Override
75 public void close() {
76 if (parentIndent != null) {
77 lastUsedIndent = parentIndent;
78 }
79 }
80 }
81
82 private static final ThreadLocal<DebugScope> instanceTL = new ThreadLocal<>();
83 private static final ThreadLocal<DebugScope> lastClosedTL = new ThreadLocal<>();
84 private static final ThreadLocal<DebugConfig> configTL = new ThreadLocal<>();
85 private static final ThreadLocal<Throwable> lastExceptionThrownTL = new ThreadLocal<>();
86
87 private final DebugScope parent;
88 private final DebugConfig parentConfig;
89 private final boolean sandbox;
90 private IndentImpl lastUsedIndent;
91 private boolean logScopeName;
92
93 private final Object[] context;
94
95 private DebugValueMap valueMap;
96
97 private String qualifiedName;
98 private final String unqualifiedName;
99
100 private static final char SCOPE_SEP = '.';
101
102 private boolean meterEnabled;
103 private boolean timeEnabled;
104 private boolean memUseTrackingEnabled;
105 private boolean verifyEnabled;
106
107 private int currentDumpLevel;
108 private int currentLogLevel;
109
110 private PrintStream output;
111
112 public static DebugScope getInstance() {
113 DebugScope result = instanceTL.get();
114 if (result == null) {
115 DebugScope topLevelDebugScope = new DebugScope(Thread.currentThread());
116 instanceTL.set(topLevelDebugScope);
117 return topLevelDebugScope;
118 } else {
119 return result;
120 }
121 }
122
123 public static DebugConfig getConfig() {
124 return configTL.get();
125 }
126
127 static final Object[] EMPTY_CONTEXT = new Object[0];
128
129 private DebugScope(Thread thread) {
130 this(thread.getName(), null, false);
131 computeValueMap(thread.getName());
132 DebugValueMap.registerTopLevel(getValueMap());
133 }
134
135 private DebugScope(String unqualifiedName, DebugScope parent, boolean sandbox, Object... context) {
136 this.parent = parent;
137 this.sandbox = sandbox;
138 this.parentConfig = getConfig();
139 this.context = context;
140 this.unqualifiedName = unqualifiedName;
141 if (parent != null) {
142 logScopeName = !unqualifiedName.equals("");
143 } else {
144 logScopeName = true;
145 }
146
147 // Be pragmatic: provide a default log stream to prevent a crash if the stream is not
148 // set while logging
149 this.output = TTY.cachedOut;
150 assert context != null;
151 }
152
153 private void computeValueMap(String name) {
154 if (parent != null) {
155 for (DebugValueMap child : parent.getValueMap().getChildren()) {
156 if (child.getName().equals(name)) {
157 this.valueMap = child;
158 return;
159 }
160 }
161 this.valueMap = new DebugValueMap(name);
162 parent.getValueMap().addChild(this.valueMap);
163 } else {
164 this.valueMap = new DebugValueMap(name);
165 }
166 }
167
168 public void close() {
169 instanceTL.set(parent);
170 configTL.set(parentConfig);
171 lastClosedTL.set(this);
172 }
173
174 public boolean isDumpEnabled(int dumpLevel) {
175 assert dumpLevel > 0;
176 return currentDumpLevel >= dumpLevel;
177 }
178
179 /**
180 * Enable dumping at the new {@code dumpLevel} for the remainder of enclosing scopes. This only
181 * works if a {@link TopLevelDebugConfig} was installed at a higher scope.
182 *
183 * @param dumpLevel
184 */
185 public static void setDumpLevel(int dumpLevel) {
186 TopLevelDebugConfig config = fetchTopLevelDebugConfig("setDebugLevel");
187 if (config != null) {
188 config.override(DelegatingDebugConfig.Level.DUMP, dumpLevel);
189 recursiveUpdateFlags();
190 }
191 }
192
193 /**
194 * Enable logging at the new {@code logLevel} for the remainder of enclosing scopes. This only
195 * works if a {@link TopLevelDebugConfig} was installed at a higher scope.
196 *
197 * @param logLevel
198 */
199 public static void setLogLevel(int logLevel) {
200 TopLevelDebugConfig config = fetchTopLevelDebugConfig("setLogLevel");
201 if (config != null) {
202 config.override(DelegatingDebugConfig.Level.LOG, logLevel);
203 config.delegate(DelegatingDebugConfig.Feature.LOG_METHOD);
204 recursiveUpdateFlags();
205 }
206 }
207
208 private static void recursiveUpdateFlags() {
209 DebugScope c = DebugScope.getInstance();
210 while (c != null) {
211 c.updateFlags();
212 c = c.parent;
213 }
214 }
215
216 private static TopLevelDebugConfig fetchTopLevelDebugConfig(String msg) {
217 DebugConfig config = getConfig();
218 if (config instanceof TopLevelDebugConfig) {
219 return (TopLevelDebugConfig) config;
220 } else {
221 if (config == null) {
222 TTY.println("DebugScope.%s ignored because debugging is disabled", msg);
223 } else {
224 TTY.println("DebugScope.%s ignored because top level delegate config missing", msg);
225 }
226 return null;
227 }
228 }
229
230 public boolean isVerifyEnabled() {
231 return verifyEnabled;
232 }
233
234 public boolean isLogEnabled(int logLevel) {
235 assert logLevel > 0;
236 return currentLogLevel >= logLevel;
237 }
238
239 public boolean isMeterEnabled() {
240 return meterEnabled;
241 }
242
243 public boolean isTimeEnabled() {
244 return timeEnabled;
245 }
246
247 public boolean isMemUseTrackingEnabled() {
248 return memUseTrackingEnabled;
249 }
250
251 public void log(int logLevel, String msg, Object... args) {
252 if (isLogEnabled(logLevel)) {
253 getLastUsedIndent().log(logLevel, msg, args);
254 }
255 }
256
257 public void dump(int dumpLevel, Object object, String formatString, Object... args) {
258 if (isDumpEnabled(dumpLevel)) {
259 DebugConfig config = getConfig();
260 if (config != null) {
261 String message = String.format(formatString, args);
262 for (DebugDumpHandler dumpHandler : config.dumpHandlers()) {
263 dumpHandler.dump(object, message);
264 }
265 }
266 }
267 }
268
269 /**
270 * This method exists mainly to allow a debugger (e.g., Eclipse) to force dump a graph.
271 */
272 public static void forceDump(Object object, String format, Object... args) {
273 DebugConfig config = getConfig();
274 if (config != null) {
275 String message = String.format(format, args);
276 for (DebugDumpHandler dumpHandler : config.dumpHandlers()) {
277 dumpHandler.dump(object, message);
278 }
279 } else {
280 TTY.println("Forced dump ignored because debugging is disabled - use -G:Dump=xxx option");
281 }
282 }
283
284 /**
285 * @see Debug#verify(Object, String)
286 */
287 public void verify(Object object, String formatString, Object... args) {
288 if (isVerifyEnabled()) {
289 DebugConfig config = getConfig();
290 if (config != null) {
291 String message = String.format(formatString, args);
292 for (DebugVerifyHandler handler : config.verifyHandlers()) {
293 handler.verify(object, message);
294 }
295 }
296 }
297 }
298
299 /**
300 * Creates and enters a new debug scope which is either a child of the current scope or a
301 * disjoint top level scope.
302 *
303 * @param name the name of the new scope
304 * @param sandboxConfig the configuration to use for a new top level scope, or null if the new
305 * scope should be a child scope
306 * @param newContextObjects objects to be appended to the debug context
307 * @return the new scope which will be exited when its {@link #close()} method is called
308 */
309 public DebugScope scope(CharSequence name, DebugConfig sandboxConfig, Object... newContextObjects) {
310 DebugScope newScope = null;
311 if (sandboxConfig != null) {
312 newScope = new DebugScope(name.toString(), this, true, newContextObjects);
313 configTL.set(sandboxConfig);
314 } else {
315 newScope = this.createChild(name.toString(), newContextObjects);
316 }
317 instanceTL.set(newScope);
318 newScope.updateFlags();
319 return newScope;
320 }
321
322 public RuntimeException handle(Throwable e) {
323 DebugScope lastClosed = lastClosedTL.get();
324 assert lastClosed.parent == this : "Debug.handle() used with no matching Debug.scope(...) or Debug.sandbox(...)";
325 if (e != lastExceptionThrownTL.get()) {
326 RuntimeException newException = null;
327 instanceTL.set(lastClosed);
328 try (DebugScope s = lastClosed) {
329 newException = s.interceptException(e);
330 }
331 assert instanceTL.get() == this;
332 assert lastClosed == lastClosedTL.get();
333 if (newException == null) {
334 lastExceptionThrownTL.set(e);
335 } else {
336 lastExceptionThrownTL.set(newException);
337 throw newException;
338 }
339 }
340 if (e instanceof Error) {
341 throw (Error) e;
342 }
343 if (e instanceof RuntimeException) {
344 throw (RuntimeException) e;
345 }
346 throw new RuntimeException(e);
347 }
348
349 private void updateFlags() {
350 DebugConfig config = getConfig();
351 if (config == null) {
352 meterEnabled = false;
353 memUseTrackingEnabled = false;
354 timeEnabled = false;
355 verifyEnabled = false;
356
357 currentDumpLevel = 0;
358
359 // Be pragmatic: provide a default log stream to prevent a crash if the stream is not
360 // set while logging
361 output = TTY.cachedOut;
362 } else {
363 meterEnabled = config.isMeterEnabled();
364 memUseTrackingEnabled = config.isMemUseTrackingEnabled();
365 timeEnabled = config.isTimeEnabled();
366 verifyEnabled = config.isVerifyEnabled();
367 output = config.output();
368 currentDumpLevel = config.getDumpLevel();
369 currentLogLevel = config.getLogLevel();
370 }
371 }
372
373 private RuntimeException interceptException(final Throwable e) {
374 final DebugConfig config = getConfig();
375 if (config != null) {
376 try (DebugScope s = scope("InterceptException", null, e)) {
377 return config.interceptException(e);
378 } catch (Throwable t) {
379 return new RuntimeException("Exception while intercepting exception", t);
380 }
381 }
382 return null;
383 }
384
385 private DebugValueMap getValueMap() {
386 if (valueMap == null) {
387 computeValueMap(unqualifiedName);
388 }
389 return valueMap;
390 }
391
392 long getCurrentValue(int index) {
393 return getValueMap().getCurrentValue(index);
394 }
395
396 void setCurrentValue(int index, long l) {
397 getValueMap().setCurrentValue(index, l);
398 }
399
400 private DebugScope createChild(String newName, Object[] newContext) {
401 return new DebugScope(newName, this, false, newContext);
402 }
403
404 public Iterable<Object> getCurrentContext() {
405 final DebugScope scope = this;
406 return new Iterable<Object>() {
407
408 @Override
409 public Iterator<Object> iterator() {
410 return new Iterator<Object>() {
411
412 DebugScope currentScope = scope;
413 int objectIndex;
414
415 @Override
416 public boolean hasNext() {
417 selectScope();
418 return currentScope != null;
419 }
420
421 private void selectScope() {
422 while (currentScope != null && currentScope.context.length <= objectIndex) {
423 currentScope = currentScope.sandbox ? null : currentScope.parent;
424 objectIndex = 0;
425 }
426 }
427
428 @Override
429 public Object next() {
430 selectScope();
431 if (currentScope != null) {
432 return currentScope.context[objectIndex++];
433 }
434 throw new IllegalStateException("May only be called if there is a next element.");
435 }
436
437 @Override
438 public void remove() {
439 throw new UnsupportedOperationException("This iterator is read only.");
440 }
441 };
442 }
443 };
444 }
445
446 public static <T> T call(Callable<T> callable) {
447 try {
448 return callable.call();
449 } catch (Exception e) {
450 if (e instanceof RuntimeException) {
451 throw (RuntimeException) e;
452 } else {
453 throw new RuntimeException(e);
454 }
455 }
456 }
457
458 public void setConfig(DebugConfig newConfig) {
459 configTL.set(newConfig);
460 updateFlags();
461 }
462
463 public String getQualifiedName() {
464 if (qualifiedName == null) {
465 if (parent == null) {
466 qualifiedName = unqualifiedName;
467 } else {
468 qualifiedName = parent.getQualifiedName() + SCOPE_SEP + unqualifiedName;
469 }
470 }
471 return qualifiedName;
472 }
473
474 public Indent pushIndentLogger() {
475 lastUsedIndent = getLastUsedIndent().indent();
476 return lastUsedIndent;
477 }
478
479 public IndentImpl getLastUsedIndent() {
480 if (lastUsedIndent == null) {
481 if (parent != null) {
482 lastUsedIndent = new IndentImpl(parent.getLastUsedIndent());
483 } else {
484 lastUsedIndent = new IndentImpl(null);
485 }
486 }
487 return lastUsedIndent;
488 }
489 }