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    }