001/* 002 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.debug; 024 025import java.util.*; 026import java.util.stream.*; 027 028/** 029 * Facility for fingerprinting execution. 030 */ 031public class Fingerprint implements AutoCloseable { 032 033 public static final String ENABLED_PROPERTY_NAME = "jvmci.fingerprint"; 034 035 /** 036 * Determines whether fingerprinting is enabled. This is set by the 037 * {@value #ENABLED_PROPERTY_NAME} system property when this class is initialized. 038 */ 039 public static final boolean ENABLED = Boolean.getBoolean(ENABLED_PROPERTY_NAME); 040 041 private static final ThreadLocal<Fingerprint> current = ENABLED ? new ThreadLocal<>() : null; 042 043 private final List<String> events; 044 private int index; 045 046 /** 047 * Creates an object to record a fingerprint. 048 */ 049 public Fingerprint() { 050 events = new ArrayList<>(); 051 index = -1; 052 } 053 054 /** 055 * Creates an object to verify execution matches a given fingerprint. 056 * 057 * @param toVerifyAgainst the fingerprint events to verify against 058 */ 059 public Fingerprint(List<String> toVerifyAgainst) { 060 this.events = toVerifyAgainst; 061 index = 0; 062 } 063 064 /** 065 * Creates an object to verify execution matches a given fingerprint. 066 * 067 * @param toVerifyAgainst the fingerprint to verify against 068 */ 069 public Fingerprint(Fingerprint toVerifyAgainst) { 070 this(toVerifyAgainst.events); 071 } 072 073 public Collection<String> getEvents() { 074 return Collections.unmodifiableCollection(events); 075 } 076 077 /** 078 * Starts fingerprint recording or verification for the current thread. At most one fingerprint 079 * object can be active for any thread. 080 */ 081 public Fingerprint open() { 082 if (ENABLED) { 083 assert current.get() == null; 084 current.set(this); 085 return this; 086 } 087 return null; 088 } 089 090 /** 091 * Finishes fingerprint recording or verification for the current thread. 092 */ 093 public void close() { 094 if (ENABLED) { 095 assert current.get() == this; 096 current.set(null); 097 } 098 } 099 100 private static final int BREAKPOINT_EVENT = Integer.getInteger(ENABLED_PROPERTY_NAME + ".breakpointEvent", -1); 101 102 /** 103 * Submits an execution event for the purpose of recording or verifying a fingerprint. This must 104 * only be called if {@link #ENABLED} is {@code true}. 105 */ 106 public static void submit(String format, Object... args) { 107 assert ENABLED : "fingerprinting must be enabled (-D" + ENABLED_PROPERTY_NAME + "=true)"; 108 Fingerprint fingerprint = current.get(); 109 if (fingerprint != null) { 110 int eventId = fingerprint.nextEventId(); 111 if (eventId == BREAKPOINT_EVENT) { 112 // Set IDE breakpoint on the following line and set the relevant 113 // system property to debug a fingerprint verification error. 114 System.console(); 115 } 116 fingerprint.event(String.format(eventId + ": " + format, args)); 117 } 118 } 119 120 private int nextEventId() { 121 return index == -1 ? events.size() : index; 122 } 123 124 private static final int MAX_EVENT_TAIL_IN_ERROR_MESSAGE = Integer.getInteger("jvmci.fingerprint.errorEventTailLength", 50); 125 126 private String tail() { 127 int start = Math.max(index - MAX_EVENT_TAIL_IN_ERROR_MESSAGE, 0); 128 return events.subList(start, index).stream().collect(Collectors.joining(String.format("%n"))); 129 } 130 131 private void event(String entry) { 132 if (index == -1) { 133 events.add(entry); 134 } else { 135 if (index > events.size()) { 136 throw new InternalError(String.format("%s%nOriginal fingerprint limit reached", tail())); 137 } 138 String l = events.get(index); 139 if (!l.equals(entry)) { 140 throw new InternalError(String.format("%s%nFingerprint differs at event %d%nexpected: %s%n actual: %s", tail(), index, l, entry)); 141 } 142 index++; 143 } 144 } 145}