001    // This file is part of the program FRYSK.
002    //
003    // Copyright 2005, 2006, 2007, 2008, Red Hat Inc.
004    //
005    // FRYSK is free software; you can redistribute it and/or modify it
006    // under the terms of the GNU General Public License as published by
007    // the Free Software Foundation; version 2 of the License.
008    //
009    // FRYSK is distributed in the hope that it will be useful, but
010    // WITHOUT ANY WARRANTY; without even the implied warranty of
011    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012    // General Public License for more details.
013    // 
014    // You should have received a copy of the GNU General Public License
015    // along with FRYSK; if not, write to the Free Software Foundation,
016    // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
017    // 
018    // In addition, as a special exception, Red Hat, Inc. gives You the
019    // additional right to link the code of FRYSK with code not covered
020    // under the GNU General Public License ("Non-GPL Code") and to
021    // distribute linked combinations including the two, subject to the
022    // limitations in this paragraph. Non-GPL Code permitted under this
023    // exception must only link to the code of FRYSK through those well
024    // defined interfaces identified in the file named EXCEPTION found in
025    // the source code files (the "Approved Interfaces"). The files of
026    // Non-GPL Code may instantiate templates or use macros or inline
027    // functions from the Approved Interfaces without causing the
028    // resulting work to be covered by the GNU General Public
029    // License. Only Red Hat, Inc. may make changes or additions to the
030    // list of Approved Interfaces. You must obey the GNU General Public
031    // License in all respects for all of the FRYSK code and other code
032    // used in conjunction with FRYSK except the Non-GPL Code covered by
033    // this exception. If you modify this file, you may extend this
034    // exception to your version of the file, but you are not obligated to
035    // do so. If you do not wish to provide this exception without
036    // modification, you must delete this exception statement from your
037    // version and license this file solely under the GPL without
038    // exception.
039    
040    package frysk.ftrace;
041    
042    import java.util.ArrayList;
043    import java.util.HashMap;
044    import java.util.HashSet;
045    import java.util.Iterator;
046    import java.util.List;
047    import java.util.Map;
048    import java.util.Set;
049    
050    import lib.dwfl.DwflModule;
051    import lib.dwfl.ModuleElfBias;
052    
053    import frysk.dwfl.ObjectFile;
054    import frysk.isa.signals.SignalTable;
055    import frysk.isa.syscalls.SyscallTable;
056    import frysk.proc.Task;
057    import frysk.rsl.Log;
058    import frysk.rsl.LogFactory;
059    import frysk.symtab.DwflSymbol;
060    import frysk.symtab.PLTEntry;
061    import frysk.symtab.SymbolFactory;
062    
063    public class FtraceController
064        implements Ftrace.Controller,
065                   Ftrace.StackTracedSymbolsProvider,
066                   Ftrace.TracedSyscallProvider,
067                   Ftrace.TracedSignalProvider
068    {
069        private static final Log warning = LogFactory.warning(FtraceController.class);
070        private static final Log fine = LogFactory.fine(FtraceController.class);
071    
072        // ArrayList<SymbolRule>
073        private final List symRules = new ArrayList();
074        private final List addrRules = new ArrayList();
075        private final List sysRules = new ArrayList();
076        private final List sigRules = new ArrayList();
077    
078        // Which symbols and addresses should yield a stack trace.
079        private HashSet tracePointStackTraceSet = new HashSet();
080        private boolean stackTraceEverything = false;
081    
082        public void stackTraceEverything() {
083            stackTraceEverything = true;
084        }
085    
086        public boolean shouldStackTraceOnTracePoint(Object tracePoint) {
087            return stackTraceEverything
088                || tracePointStackTraceSet.contains(tracePoint);
089        }
090    
091        public FtraceController() { }
092    
093        public void gotSymRules(List rules) {
094            fine.log("Got " + rules.size() + " symbol rules.");
095            this.symRules.addAll(rules);
096        }
097    
098        public void gotAddrRules(List rules) {
099            fine.log("Got " + rules.size() + " address rules.");
100            this.addrRules.addAll(rules);
101        }
102    
103        public void gotSysRules(List rules) {
104            fine.log("Got " + rules.size() + " syscall rules.");
105            this.sysRules.addAll(rules);
106        }
107    
108        public void gotSigRules(List rules) {
109            fine.log("Got " + rules.size() + " signal rules.");
110            this.sigRules.addAll(rules);
111        }
112    
113        private Map computeWorkingSet(Task task, String what,
114                                      List rules, ArrayList candidates)
115        {
116            HashSet workingSet = new HashSet();
117            HashSet stackTraceSet = new HashSet();
118    
119            for (Iterator it = rules.iterator(); it.hasNext(); ) {
120                final Rule rule = (Rule)it.next();
121                fine.log("Considering " + what + " rule `" + rule + "'.");
122                if (!rule.apply(candidates, workingSet, stackTraceSet))
123                    warning.log("Rule", rule, "didn't match any", what);
124            }
125    
126            // Apply the two sets.
127            Map ret = new HashMap();
128            for (Iterator it = workingSet.iterator(); it.hasNext(); ) {
129                Object sysOrSig = it.next();
130                ret.put(sysOrSig,
131                        Boolean.valueOf(stackTraceEverything
132                                        || stackTraceSet.contains(sysOrSig)));
133            }
134            return ret;
135        }
136    
137        // Syscall working and stack trace sets can be pre-computed for
138        // each task.  This is in contrast to tracing rules, that are
139        // computed incrementally when DSOs are mapped.
140        public Map computeSyscallWorkingSet(Task task) {
141            SyscallTable syscallTable = task.getSyscallTable();
142            long n = syscallTable.getNumSyscalls();
143            ArrayList candidates = new ArrayList();
144            for (long i = 0; i < n; ++i)
145                candidates.add(syscallTable.getSyscall(i));
146    
147            return computeWorkingSet(task, "syscall", sysRules, candidates);
148        }
149    
150        // Compute signal working and stack trace sets.
151        public Map computeSignalWorkingSet(Task task) {
152            frysk.sys.Signal[] hostSignals
153                = frysk.sys.Signal.getHostSignalSet().toArray();
154            SignalTable signalTable = task.getSignalTable();
155            ArrayList candidates = new ArrayList();
156            for (int i = 0; i < hostSignals.length; i++)
157                candidates.add(signalTable.get(hostSignals[i].intValue()));
158    
159            return computeWorkingSet(task, "signal", sigRules, candidates);
160        }
161    
162        private boolean isInterpOf(ObjectFile objf, String exe)
163        {
164            ObjectFile exef = ObjectFile.buildFromFile(exe);
165            java.io.File interpfn = exef.resolveInterp();
166            java.io.File objffn = objf.getFilename();
167            return objffn.equals(interpfn);
168        }
169    
170        interface RuleHandler {
171            void applyTracePoint(Object tracePoint);
172        }
173    
174        private void applySymbolRules(final Task task, final ObjectFile objf,
175                                      final List tracePoints,
176                                      final List rules, RuleHandler handler)
177            throws lib.dwfl.ElfException
178        {
179            String path = objf.getFilename().getPath();
180            fine.log("Building working set for task", task, "and path", path);
181    
182            // Skip the set if it's empty...
183            if (rules.isEmpty())
184                return;
185    
186            // Set<DwflSymbol>, incrementally built working set.
187            final Set workingSet = new HashSet();
188            // Set<DwflSymbol>, incrementally built set of tracepoints
189            // that should stacktrace.
190            final Set stackTraceSet = new HashSet();
191    
192            // Loop through all the rules, and use them to build
193            // workingSet from candidates.
194            for (Iterator it = rules.iterator(); it.hasNext(); ) {
195                final SymbolRule rule = (SymbolRule)it.next();
196                fine.log("Considering symbol rule " + rule + ".");
197    
198                if (rule.fqid.sonameMatches(task, objf))
199                    rule.apply(tracePoints, workingSet, stackTraceSet);
200            }
201    
202            // Finally, apply constructed working set.
203            fine.log("Applying working set for ", path);
204            tracePointStackTraceSet.addAll(stackTraceSet);
205            for (Iterator it = workingSet.iterator(); it.hasNext(); )
206                handler.applyTracePoint(it.next());
207        }
208    
209        private void applyAddrRules(final Task task, final ObjectFile objf,
210                                    long bias,
211                                    final List rules, final Ftrace.Driver driver)
212        {
213            String path = objf.getFilename().getPath();
214            fine.log("Building checkpoint set for task", task, "and path", path);
215    
216            // Skip the set if it's empty...
217            if (rules.isEmpty())
218                return;
219    
220            // Set<Long>, incrementally built working set.
221            final Set workingSet = new HashSet();
222            // Set<Long>, incrementally built set of tracepoints
223            // that should stacktrace.
224            final Set stackTraceSet = new HashSet();
225    
226            // Loop through all the rules, and use them to build
227            // workingSet from candidates.
228            for (Iterator it = rules.iterator(); it.hasNext(); ) {
229                final AddrRule rule = (AddrRule)it.next();
230                final List candidate = new ArrayList(1);
231                candidate.add(new Long(rule.addr));
232    
233                fine.log("Considering addr rule", rule);
234    
235                // MAIN is meta-soname meaning "main executable".
236                if ((rule.sonamePattern.pattern().equals("MAIN")
237                     && task.getProc().getExeFile().getSysRootedPath().equals(path))
238                    || (rule.sonamePattern.pattern().equals("INTERP")
239                        && isInterpOf(objf, task.getProc().getExeFile().getSysRootedPath()))
240                    || rule.sonamePattern.matcher(objf.getSoname()).matches())
241                {
242                    rule.apply(candidate, workingSet, stackTraceSet);
243                }
244            }
245    
246            // Finally, apply constructed working set.
247            fine.log("Applying checkpoint set for ", path);
248            tracePointStackTraceSet.addAll(stackTraceSet);
249            for (Iterator it = workingSet.iterator(); it.hasNext(); ) {
250                Long token = (Long)it.next();
251                driver.traceAddress(task, token, bias, objf);
252            }
253        }
254    
255        private List traceablesForModule(DwflModule module) {
256    
257            Map symbolTable = SymbolFactory.getSymbolTable(module);
258            if (symbolTable.size() == 0)
259                // In that case we also know there are no PLT entries,
260                // because each PLT entry is defined on a symbol.
261                return null;
262    
263            List traceables = new ArrayList(symbolTable.values());
264            traceables.addAll(SymbolFactory.getPLTEntries(module, symbolTable));
265    
266            return traceables;
267        }
268    
269        public void fileMapped(final Task task, ObjectFile objf,
270                               DwflModule module,
271                               final Ftrace.Driver driver) {
272    
273            List traceables = traceablesForModule(module);
274            if (traceables == null)
275                return;
276    
277            try {
278                applySymbolRules
279                    (task, objf, traceables, symRules,
280                     new RuleHandler() {
281                         public void applyTracePoint(Object tracePoint) {
282                             if (tracePoint instanceof PLTEntry) {
283                                 PLTEntry entry = (PLTEntry)tracePoint;
284                                 driver.tracePLTEntry(task, entry);
285                             } else {
286                                 DwflSymbol symbol = (DwflSymbol)tracePoint;
287                                 driver.traceSymbol(task, symbol);
288                             }
289                         }
290                     });
291    
292                ModuleElfBias eb = module.getElf();
293                applyAddrRules(task, objf, eb.bias, addrRules, driver);
294    
295            } catch (lib.dwfl.ElfException ee) {
296                ee.printStackTrace();
297            }
298        }
299    
300        public void fileUnmapped(final Task task, ObjectFile objf,
301                                 DwflModule module,
302                                 final Ftrace.Driver driver) {
303    
304            List traceables = traceablesForModule(module);
305            if (traceables == null)
306                return;
307    
308            driver.untrace(task, traceables);
309        }
310    
311    }