001 // DtracePartitioner.java
002 package daikon.tools;
003 import java.util.*;
004 import java.io.*;
005 import plume.*;
006
007 /** This class partitions Daikon trace files so that invocations of
008 * the same program point are grouped together for use with random
009 * selection.
010 */
011
012 public class DtracePartitioner
013 implements Partitioner<String,String>, Iterator<String>
014 {
015
016 @SuppressWarnings("nullness") // line.separator property always exists
017 private static final String lineSep = System.getProperty("line.separator");
018
019 // reading from the file as a lazy iterator
020 private BufferedReader br;
021 // the name of the Daikon trace file
022 private String fileName;
023
024 /** @param filename The Daikon trace file to be partitioned
025 */
026 public DtracePartitioner (String filename) {
027 try {
028 this.fileName = filename;
029 // System.out.printf ("trying with file %s%n", fileName);
030 br = UtilMDE.bufferedFileReader (fileName);
031
032 } catch (IOException e) {e.printStackTrace(); }
033 }
034
035 public boolean hasNext() {
036 try {
037 return br.ready();
038 } catch (IOException e) {e.printStackTrace(); return false; }
039 }
040
041 /** Not implemented, because this class does not modify the underlying
042 trace file. */
043 public void remove () {
044 throw new UnsupportedOperationException("Can not remove");
045 }
046
047
048
049 public String next() {
050 try {
051 String ret = grabNextInvocation ();
052 if (ret.indexOf ("EXIT") != -1) {
053 if (!br.ready()) return "";
054 return next();
055 }
056 else return ret;
057 } catch (IOException e) {e.printStackTrace(); }
058 throw new RuntimeException ("Should never reach this statement");
059
060 }
061
062 /** Grabs the next invocation in the Daikon trace file by interpreting
063 * a blank line as the invocation delimter. Note that multiple blank
064 * lines between invocations might occur, so the callee is responsible
065 * for checking if the returned String is a blank line */
066 private String grabNextInvocation () throws IOException {
067 StringBuffer sb = new StringBuffer();
068 while (br.ready()) {
069 String line = br.readLine();
070 assert line != null; // because br.ready() = true
071 line = line.trim();
072 if (line.equals ("")) {
073 break;
074 }
075 sb.append(line).append (lineSep);
076 }
077 return sb.toString();
078 }
079
080
081 /** Returns the program point name given by the input invocation. */
082 public String assignToBucket (String invocation) {
083 if (invocation.indexOf (lineSep) == -1) {
084 // was: return null;
085 throw new Error("No lineSep: " + invocation);
086 }
087 return invocation.substring (0, invocation.indexOf (lineSep));
088 }
089
090 /** Same as {@link #patchValues (List<String>, boolean)} with second arg=false. */
091 public List<String> patchValues (List<String> enters) {
092 return patchValues (enters, false);
093 }
094
095 /** Finds the exits that correspond to Enters.
096 * <br>Modifies: none
097 * <br>Returns: An ArrayList containing all of the elements of 'enters'
098 * <br> @param includeUnreturnedEnters
099 * ensures that any ENTER ppt invocations will definitely have
100 * a corresponding EXIT ppt invocation following them.
101 * <p> The original order is NOT guaranteed.
102 */
103
104 public List<String> patchValues (List<String> enters, boolean includeUnreturnedEnters) {
105 try {
106 System.out.println ("Entering patchValues");
107 // Keep a list of enters that are so far unmatched
108 Set<String> unreturned = new HashSet<String> (enters);
109
110 // Build a hashmap of values to watch
111 HashMap<Object/*String or Integer*/,String> nonceMap = new HashMap<Object,String>();
112 for (String enterStr : enters) {
113 // it could be an OBJECT or CLASS invocation ppt, ignore those
114 // by putting them in the HashMap to themselves, they'll
115 // be reaped up later
116 if (enterStr.indexOf ("ENTER") == -1) {
117 nonceMap.put (enterStr, enterStr);
118 // no way for OBJECT or CLASS to be unresolved
119 unreturned.remove (enterStr);
120 continue;
121 }
122
123 // get the nonce of this invocation and use it
124 // as the key in the nonceMap, which maps
125 // nonces --> ENTER half of invocation
126 int theNonce = calcNonce (enterStr);
127 nonceMap.put (new Integer (theNonce), enterStr);
128 }
129
130
131
132 // look for EXIT half of invocations and augment
133 // the values of nonceMap so that the map eventually
134 // maps nonces --> full invocations with ENTER / EXIT
135 br = UtilMDE.bufferedFileReader(fileName);
136 while (br.ready()) {
137 String nextInvo = grabNextInvocation();
138 if (nextInvo.indexOf ("EXIT") == -1) continue;
139 int invoNonce = calcNonce (nextInvo);
140 Integer key = new Integer (invoNonce);
141 String enterInvo = nonceMap.get (key);
142 if (enterInvo != null) {
143 nonceMap.put (key, enterInvo + lineSep + nextInvo);
144 unreturned.remove (enterInvo);
145 }
146 }
147
148 // Return a list of all the invocations where matching ENTER and
149 // EXIT points were found as well as the OBJECT and CLASS
150 // invocations.
151 ArrayList<String> al = new ArrayList<String>();
152 for (String s : nonceMap.values()) {
153 al.add (s);
154 }
155 // add in the invocations that were never resolved because no
156 // matching EXIT invocation exists.
157 if (!includeUnreturnedEnters) {
158 al.removeAll (unreturned);
159 }
160 return al;
161
162
163 } catch (IOException e) {e.printStackTrace(); }
164 return enters;
165 }
166
167 private int calcNonce (String invocation) {
168 StringTokenizer st = new StringTokenizer (invocation, lineSep);
169 while (st.hasMoreTokens()) {
170 String line = st.nextToken();
171 if (line.equals ("this_invocation_nonce"))
172 return Integer.parseInt (st.nextToken());
173 }
174 throw new RuntimeException ("This invocation didn't contain a nonce: "
175 + invocation);
176
177 }
178
179 }