001 package daikon.split;
002
003 import java.io.*;
004 import java.util.*;
005 import daikon.split.misc.*;
006 import plume.*;
007 import java.util.logging.Logger;
008
009 /**
010 * This factory creates Splitters from map files. The splitters
011 * partition the data based upon the the caller (i.e., which static
012 * callgraph edge was taken).
013 **/
014 public class ContextSplitterFactory
015 {
016 /** Debug tracer. **/
017 public static final Logger debug = Logger.getLogger("daikon.split.ContextSplitterFactory");
018
019 /** Callsite granularity at the line level. */
020 public static final int GRAIN_LINE = 0;
021 /** Callsite granularity at the method level. */
022 public static final int GRAIN_METHOD = 1;
023 /** Callsite granularity at the class level. */
024 public static final int GRAIN_CLASS = 2;
025
026 // Variables starting with dkconfig_ should only be set via the
027 // daikon.config.Configuration interface.
028 /**
029 * Enumeration (integer). Specifies the granularity to use for
030 * callsite splitter processing. 0 is line-level granularity; 1 is
031 * method-level granularity; 2 is class-level granularity.
032 **/
033 public static int dkconfig_granularity = GRAIN_METHOD;
034
035 /**
036 * @param files set of File objects to read from
037 * @param grain one of the GRAIN constants defined in this class
038 *
039 * Read all the map files in the given collection, create callsite
040 * splitters from them, and put the splitters into SplitterList.
041 **/
042 public static void load_mapfiles_into_splitterlist(Collection<File> files,
043 int grain
044 ) {
045 for (File file : files) {
046 String filename = file.getName();
047
048 System.out.print("."); // show progress
049 debug.fine ("Reading mapfile " + filename);
050
051 PptNameAndSplitters[] splitters;
052 try {
053 MapfileEntry[] entries = parse_mapfile(file);
054 splitters = make_context_splitters(entries, grain);
055 } catch (IOException e) {
056 throw new Error(e);
057 }
058
059 for (int j=0; j < splitters.length; j++) {
060 PptNameAndSplitters nas = splitters[j];
061 SplitterList.put(nas.ppt_name, nas.splitters);
062 }
063 }
064 }
065
066 /**
067 * Simple record type to store a map file entry.
068 **/
069 public static final class MapfileEntry
070 {
071 public final long id;
072 public final String fromclass;
073 public final String frommeth;
074 public final String fromfile;
075 public final long fromline;
076 public final long fromcol;
077 public final String toexpr;
078 public final String toargs;
079 public final String toclass;
080 public final String tometh;
081
082 public MapfileEntry(long id,
083 String fromclass,
084 String frommeth,
085 String fromfile,
086 long fromline,
087 long fromcol,
088 String toexpr,
089 String toargs,
090 String toclass,
091 String tometh)
092 {
093 this.id = id;
094 this.fromclass = fromclass;
095 this.frommeth = frommeth;
096 this.fromfile = fromfile;
097 this.fromline = fromline;
098 this.fromcol = fromcol;
099 this.toexpr = toexpr;
100 this.toargs = toargs;
101 this.toclass = toclass;
102 this.tometh = tometh;
103 }
104 }
105
106 /**
107 * Read and parse a map file.
108 **/
109 public static MapfileEntry[] parse_mapfile(File mapfile)
110 throws IOException
111 {
112 ArrayList<MapfileEntry> result = new ArrayList<MapfileEntry>();
113
114 try {
115 for (String reader_line : new EntryReader(mapfile.toString())) {
116 String line = reader_line;
117 // Remove comments, skip blank lines
118 {
119 int hash = line.indexOf('#');
120 if (hash >= 0) {
121 line = line.substring(0, hash);
122 }
123 line = line.trim();
124 if (line.length() == 0) {
125 continue;
126 }
127 }
128
129 // Example line:
130 // 0x85c2e8c PC.RPStack get [PC/RPStack.java:156:29] -> "getCons" [(I)LPC/Cons;] PC.RP meth
131 // where this ^ is a tab and the rest are single spaces
132 long id;
133 String fromclass, frommeth, fromfile; long fromline, fromcol;
134 String toexpr, toargs, toclass, tometh;
135
136 int tab = line.indexOf('\t');
137 int arrow = line.indexOf(" -> ");
138 assert tab >= 0;
139 assert arrow >= tab;
140
141 id = Long.decode(line.substring(0, tab)).longValue();
142
143 // parse "called from" data
144 {
145 StringTokenizer tok = new StringTokenizer(line.substring(tab+1,arrow));
146 fromclass = tok.nextToken();
147 frommeth = tok.nextToken();
148 String temp = tok.nextToken();
149 assert temp.startsWith("[");
150 assert temp.endsWith("]");
151 temp = temp.substring(1, temp.length()-1);
152 int one = temp.indexOf(':');
153 int two = temp.lastIndexOf(':');
154 fromfile = temp.substring(0, one);
155 fromline = Integer.parseInt(temp.substring(one+1,two));
156 fromcol = Integer.parseInt(temp.substring(two+1));
157 assert ! tok.hasMoreTokens();
158 }
159
160 // parse "call into" data
161 {
162 String to = line.substring(arrow + 4); // 4: " -> "
163 assert to.startsWith("\"") : to;
164 int endquote = to.indexOf("\" ", 1);
165 toexpr = line.substring(1, endquote);
166 StringTokenizer tok = new StringTokenizer(to.substring(endquote+1));
167 toargs = tok.nextToken();
168 toclass = tok.nextToken();
169 tometh = tok.nextToken();
170 assert ! tok.hasMoreTokens();
171 }
172
173 MapfileEntry entry = new MapfileEntry
174 (id, fromclass, frommeth, fromfile, fromline, fromcol,
175 toexpr, toargs, toclass, tometh);
176
177 result.add(entry);
178 }
179 } catch (NumberFormatException e) {
180 throw (IOException)(new IOException("Malformed number").initCause(e));
181 }
182
183 return result.toArray(new MapfileEntry[result.size()]);
184 }
185
186 /**
187 * @param grain one of the GRAIN constants defined in this class
188 *
189 * Given map file data, create splitters given the requested
190 * granularity.
191 **/
192 public static PptNameAndSplitters[] make_context_splitters(MapfileEntry[] entries,
193 int grain) {
194 // Use a 2-deep map structure. First key is an identifier
195 // (~pptname) for the callee. Second key is an idenfier for the
196 // caller (based on granularity). The value is a set of Integers
197 // giving the ids that are associated with that callgraph edge.
198 Map<String,Map<String,Set<Long>>> callee2caller2ids = new HashMap<String,Map<String,Set<Long>>>();
199
200 // For each entry
201 for (int i=0; i < entries.length; i++) {
202 MapfileEntry entry = entries[i];
203 String callee_ppt_name = entry.toclass + "." + entry.tometh;
204
205 // Compute the caller based on granularity
206 String caller_condition;
207 switch (grain) {
208 case GRAIN_LINE:
209 caller_condition = "<Called from "
210 + entry.fromclass + "." + entry.frommeth
211 + ":" + entry.fromline + ":" + entry.fromcol + ">";
212 break;
213 case GRAIN_METHOD:
214 caller_condition = "<Called from "
215 + entry.fromclass + "." + entry.frommeth + ">";
216 break;
217 case GRAIN_CLASS:
218 caller_condition = "<Called from "
219 + entry.fromclass + ">";
220 break;
221 default:
222 throw new UnsupportedOperationException("Unknown grain " + grain);
223 }
224
225 // Place the ID into the mapping
226 Map<String,Set<Long>> caller2ids = callee2caller2ids.get(callee_ppt_name);
227 if (caller2ids == null) {
228 caller2ids = new HashMap<String,Set<Long>>();
229 callee2caller2ids.put(callee_ppt_name, caller2ids);
230 }
231 Set<Long> ids = caller2ids.get(caller_condition);
232 if (ids == null) {
233 ids = new TreeSet<Long>();
234 caller2ids.put(caller_condition, ids);
235 }
236 ids.add(new Long(entry.id));
237 } // for all entries
238
239 ArrayList<PptNameAndSplitters> result = new ArrayList<PptNameAndSplitters>();
240
241 // For each callee
242 for (Map.Entry<String,Map<String,Set<Long>>> ipair : callee2caller2ids.entrySet()) {
243 String callee_ppt_name = ipair.getKey();
244 Map<String,Set<Long>> caller2ids = ipair.getValue();
245
246 // 'splitters' collects all splitters for one callee_ppt_name
247 Collection<Splitter> splitters = new ArrayList<Splitter>();
248
249 // For each caller of that callee
250 for (Map.Entry<String,Set<Long>> jpair : caller2ids.entrySet()) {
251 String caller_condition = jpair.getKey();
252 List<Long> ids = new ArrayList<Long>(jpair.getValue());
253
254 // Make a splitter
255 long[] ids_array = new long[ids.size()];
256 for (int k=0; k < ids_array.length; k++) {
257 ids_array[k] = ids.get(k).longValue();
258 }
259
260 debug.fine ("Creating splitter for " + callee_ppt_name
261 + " with ids " + ids
262 + " named " + caller_condition);
263
264 Splitter splitter = new CallerContextSplitter(ids_array, caller_condition);
265 splitters.add(splitter);
266 }
267
268 // Collect all splitters for one callee_ppt_name
269 Splitter[] splitters_array =
270 splitters.toArray(new Splitter[splitters.size()]);
271 result.add(new PptNameAndSplitters(callee_ppt_name, splitters_array));
272 }
273
274 return
275 result.toArray(new PptNameAndSplitters[result.size()]);
276 }
277
278 /**
279 * Simple record type to store a PptName and Splitter array.
280 **/
281 public static final class PptNameAndSplitters
282 {
283 public final String ppt_name; // really more like a regexp
284 public final Splitter[] splitters;
285
286 public PptNameAndSplitters(String ppt_name, Splitter[] splitters) {
287 this.ppt_name = ppt_name;
288 this.splitters = splitters;
289 }
290
291 public String toString() {
292 return "PptNameAndSplitters<" + ppt_name + ","
293 + Arrays.asList(splitters).toString() + ">";
294 }
295
296 }
297
298 }