001    package railo.runtime.debug;
002    
003    import java.io.IOException;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.HashMap;
007    import java.util.Iterator;
008    import java.util.List;
009    import java.util.ListIterator;
010    import java.util.Map;
011    
012    import railo.commons.io.log.LogUtil;
013    import railo.commons.lang.StringUtil;
014    import railo.runtime.PageContext;
015    import railo.runtime.PageSource;
016    import railo.runtime.config.Config;
017    import railo.runtime.config.ConfigWebImpl;
018    import railo.runtime.db.SQL;
019    import railo.runtime.dump.DumpData;
020    import railo.runtime.dump.DumpProperties;
021    import railo.runtime.dump.DumpRow;
022    import railo.runtime.dump.DumpTable;
023    import railo.runtime.dump.DumpUtil;
024    import railo.runtime.dump.Dumpable;
025    import railo.runtime.dump.SimpleDumpData;
026    import railo.runtime.exp.CatchBlock;
027    import railo.runtime.exp.DatabaseException;
028    import railo.runtime.exp.PageException;
029    import railo.runtime.exp.PageExceptionImpl;
030    import railo.runtime.interpreter.CFMLExpressionInterpreter;
031    import railo.runtime.op.Caster;
032    import railo.runtime.type.Array;
033    import railo.runtime.type.ArrayImpl;
034    import railo.runtime.type.Collection;
035    import railo.runtime.type.DebugQueryColumn;
036    import railo.runtime.type.KeyImpl;
037    import railo.runtime.type.Query;
038    import railo.runtime.type.QueryColumn;
039    import railo.runtime.type.QueryImpl;
040    import railo.runtime.type.Struct;
041    import railo.runtime.type.StructImpl;
042    
043    
044    /**
045     * Class to debug the application
046     */
047    public final class DebuggerImpl implements Dumpable, Debugger {
048    
049            private static final long serialVersionUID = 4103405920561543718L;
050            private static final Collection.Key PAGES = KeyImpl.intern("pages");
051            private static final Collection.Key QUERIES = KeyImpl.intern("queries");
052            private static final Collection.Key TIMERS = KeyImpl.intern("timers");
053            private static final Collection.Key TRACES = KeyImpl.intern("traces");
054            private static final Collection.Key HISTORY = KeyImpl.intern("history");
055            private static final Collection.Key SQL = KeyImpl.intern("sql");
056            private static final Collection.Key SRC = KeyImpl.intern("src");
057            private static final Collection.Key COUNT = KeyImpl.intern("count");
058            private static final Collection.Key DATASOURCE = KeyImpl.intern("datasource");
059            private static final Collection.Key USAGE = KeyImpl.intern("usage");
060            
061            private Map<String,DebugEntryImpl> pages=new HashMap<String,DebugEntryImpl>();
062            private List<QueryEntryImpl> queries=new ArrayList<QueryEntryImpl>();
063            private List<DebugTimerImpl> timers=new ArrayList<DebugTimerImpl>();
064            private List<DebugTraceImpl> traces=new ArrayList<DebugTraceImpl>();
065            private List<CatchBlock> exceptions=new ArrayList<CatchBlock>();
066            
067            private boolean output=true;
068            private long lastEntry;
069            private long lastTrace;
070            private Array historyId=new ArrayImpl();
071            private Array historyLevel=new ArrayImpl();
072            
073            /**
074         * @see railo.runtime.debug.Debugger#reset()
075         */
076            public void reset() {
077                    pages.clear();
078                    queries.clear();
079                    timers.clear();
080                    traces.clear();
081                    exceptions.clear();
082                    historyId.clear();
083                    historyLevel.clear();
084                    output=true;
085            }
086    
087            /**
088             * standart Constructor of the class
089             */
090            public DebuggerImpl() {
091                    
092            }
093    
094        /**
095         *
096         * @see railo.runtime.debug.Debugger#getEntry(railo.runtime.PageContext, railo.runtime.PageSource)
097         */
098        public DebugEntry getEntry(PageContext pc,PageSource source) {
099            return getEntry(pc,source,null);
100        }
101    
102        /**
103         *
104         * @see railo.runtime.debug.Debugger#getEntry(railo.runtime.PageContext, railo.runtime.PageSource, java.lang.String)
105         */
106        public DebugEntry getEntry(PageContext pc,PageSource source, String key) {
107            lastEntry = System.currentTimeMillis();
108            String src=DebugEntryImpl.getSrc(source,key);
109            
110            DebugEntryImpl de=(DebugEntryImpl) pages.get(src);
111            if(de!=null ){
112                de.countPP();
113                            historyId.appendEL(de.getId());
114                            historyLevel.appendEL(Caster.toInteger(pc.getCurrentLevel()));
115                return de;
116            }
117            de=new DebugEntryImpl(source,key);
118            pages.put(src,de);
119                    historyId.appendEL(de.getId());
120                    historyLevel.appendEL(Caster.toInteger(pc.getCurrentLevel()));
121            return de;
122        }
123    
124        /**
125             * @see railo.runtime.dump.Dumpable#toDumpData(railo.runtime.PageContext, int)
126             */
127            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
128                    if(!output) return null;
129                    
130            // fill pages to aray
131            ArrayList<DebugEntry> arrPages = toArray();
132            QueryEntry[] arrQueries=(QueryEntry[]) queries.toArray(new QueryEntry[queries.size()]);
133    
134                    
135                    DumpTable boxPage = new DumpTable("#cccccc","#ffffff","#000000");
136                    boxPage.setWidth("100%");
137                    
138            int len=arrPages.size();
139            int tLoad=0;
140            int tQuery=0;
141            int tApp=0;
142            int tCount=0;
143            for(int i=0;i<len;i++) {
144                DebugEntry de=(DebugEntry) arrPages.get(i);
145                tLoad+=de.getFileLoadTime();
146                tQuery+=de.getQueryTime();
147                tApp+=de.getExeTime();
148                tCount+=de.getCount();
149                boxPage.appendRow(new DumpRow(0,new DumpData[]{
150                        new SimpleDumpData(de.getSrc()),
151                        new SimpleDumpData(String.valueOf(de.getCount())),
152                        //plus(de.getMin()),//Min
153                        //plus(de.getExeTime()/de.getCount()),
154                        //plus(de.getMax()),//Max
155                        _toDumpData(de.getFileLoadTime()),
156                        _toDumpData(de.getQueryTime()),
157                        new SimpleDumpData(_toDumpData(de.getExeTime()-de.getQueryTime())+""),
158                        new SimpleDumpData(""),
159                        _toDumpData(de.getFileLoadTime()+de.getExeTime())}));
160                            
161                    }
162            // Total
163            DumpRow row = new DumpRow(1023,new DumpData[]{new SimpleDumpData("Total"),_toDumpData(tCount),_toDumpData(tLoad),_toDumpData(tQuery),_toDumpData(tApp-tQuery),new SimpleDumpData(""),_toDumpData(tLoad+tApp)});
164            boxPage.appendRow(row);
165            boxPage.prependRow(row);
166            
167            row=new DumpRow(1023,new DumpData[]{new SimpleDumpData("file"),new SimpleDumpData("count"),new SimpleDumpData("load"),new SimpleDumpData("query"),new SimpleDumpData("app"),new SimpleDumpData(""),new SimpleDumpData("total")});
168            boxPage.appendRow(row);
169            boxPage.prependRow(row);
170    
171    //      Exceptions
172            DumpTable tableExceptions=null;
173                    int tl=exceptions==null?0:exceptions.size();
174                    if(tl>0) {
175                            tableExceptions = new DumpTable("#cccccc","#ffffff","#000000");
176                            
177                            tableExceptions.appendRow(15, new SimpleDumpData("type"),new SimpleDumpData("message"),new SimpleDumpData("detail"), new SimpleDumpData("template"));
178                            
179                            
180                            Iterator<CatchBlock> it = exceptions.iterator();
181                            CatchBlock block;
182                            PageException pe;
183                            String type,msg,detail,templ;
184                            while(it.hasNext()) {
185                                    block=it.next();
186                                    pe=block.getPageException();
187                                    type=StringUtil.emptyIfNull(pe.getTypeAsString());
188                                    msg=StringUtil.emptyIfNull(pe.getMessage());
189                                    detail=StringUtil.emptyIfNull(pe.getDetail());
190                                    templ=getTemplate(pageContext.getConfig(),pe,true);
191                                    tableExceptions.appendRow(0, 
192                                                    new SimpleDumpData(type),
193                                                    new SimpleDumpData(msg),
194                                                    new SimpleDumpData(detail),
195                                                    new SimpleDumpData(templ)) ;
196                            }
197                    }
198                    
199    //      Timers
200            DumpTable tableTimer=null;
201                    tl=timers==null?0:timers.size();
202                    if(tl>0) {
203                            tableTimer = new DumpTable("#cccccc","#ffffff","#000000");
204                            //boxTimer.setWidth("100%");
205                            tableTimer.appendRow(7, new SimpleDumpData("label"), new SimpleDumpData("time (ms)"),new SimpleDumpData("template"));
206                            
207                            
208                            Iterator<DebugTimerImpl> it = timers.iterator();
209                            DebugTimer timer;
210                            while(it.hasNext()) {
211                                    timer=(DebugTimer) it.next();
212                                    tableTimer.appendRow(0, new SimpleDumpData(timer.getLabel()),new SimpleDumpData(timer.getTime()),new SimpleDumpData(timer.getTemplate())) ;
213                            }
214                    }
215    
216    //      Traces
217                    DumpTable tableTraces=null;
218                    tl=traces==null?0:traces.size();
219                    if(tl>0) {
220                            tableTraces = new DumpTable("#cccccc","#ffffff","#000000");
221                            tableTraces.setWidth("100%");
222                            tableTraces.appendRow(
223                                            new DumpRow(2047, 
224                                                            new DumpData[]{
225                                                            new SimpleDumpData("type"), 
226                                                            new SimpleDumpData("category"),
227                                                            new SimpleDumpData("text"),
228                                                            new SimpleDumpData("template"),
229                                                            new SimpleDumpData("line"),
230                                                            new SimpleDumpData("action"),
231                                                            new SimpleDumpData("var name"),
232                                                            new SimpleDumpData("var value"),
233                                                            new SimpleDumpData("total time (ms)"),
234                                                            new SimpleDumpData("trace slot time (ms)")}));
235                    
236                    Iterator<DebugTraceImpl> it = traces.iterator();
237                            DebugTraceImpl trace;
238                            int total=0;
239                            while(it.hasNext()) {
240                                    trace=(DebugTraceImpl) it.next();
241                                    total+=trace.getTime();
242                                    DumpTable tableVar=new DumpTable("#cccccc","#ffffff","#000000");
243                                    SimpleDumpData varValue = new SimpleDumpData(toString(trace.getVarValue()));
244                                    DumpData var;
245                                    try {
246                                            Object value = new CFMLExpressionInterpreter().interpret(pageContext,toString(trace.getVarValue()));
247                                            tableVar.appendRow(0, varValue);
248                                            tableVar.appendRow(0,DumpUtil.toDumpData(value, pageContext, maxlevel,dp) );
249                                            var=tableVar;
250                                    }
251                                    catch(Throwable t) {
252                                            var=varValue;
253                                    }
254                                    
255                                    tableTraces.appendRow(new DumpRow(0, new DumpData[]{
256                                                    new SimpleDumpData(LogUtil.toStringType(trace.getType(), "INFO")),
257                                                    new SimpleDumpData(toString(trace.getCategory())),
258                                                    new SimpleDumpData(toString(trace.getText())),
259                                                    new SimpleDumpData(toString(trace.getTemplate())),
260                                                    new SimpleDumpData(Caster.toString(trace.getLine())),
261                                                    new SimpleDumpData(trace.getAction()),
262                                                    new SimpleDumpData(toString(trace.getVarName())),
263                                                    var,
264                                                    new SimpleDumpData(Caster.toString(total)),
265                                                    new SimpleDumpData(Caster.toString(trace.getTime()))}));
266                            }
267                    }
268                    
269    //               Query
270                    DumpTable tableQuery=null;
271                    DumpTable tableQueryItem = null;
272                    if(arrQueries.length>0) {
273                            
274                            tableQuery = new DumpTable("#cccccc","#ffffff","#000000");
275                            tableQuery.setWidth("100%");
276                            
277                            for(int i=0;i<arrQueries.length;i++) {
278                                    tableQueryItem = new DumpTable("#cccccc","#ffffff","#000000");
279                                    tableQueryItem.appendRow(1, new SimpleDumpData("Source"), new SimpleDumpData(arrQueries[i].getSrc()));
280                                    tableQueryItem.appendRow(1, new SimpleDumpData("Execution Time"), new SimpleDumpData(arrQueries[i].getExe()));
281                                    tableQueryItem.appendRow(1, new SimpleDumpData("Recordcount"), new SimpleDumpData(arrQueries[i].getRecordcount()));
282                                    tableQueryItem.appendRow(1, new SimpleDumpData("SQL"), new SimpleDumpData((arrQueries[i].getSQL().toString().trim())));
283                                    
284                                    // usage
285                                    try {
286                                            String usage = getUsageList(arrQueries[i]);
287                                            if(!StringUtil.isEmpty(usage)) {
288                                    tableQueryItem.appendRow(1, new SimpleDumpData("Columns not read"), new SimpleDumpData(usage)); 
289                            }
290                                    } catch (PageException e) {}
291                    
292                    
293                                    
294                                    
295                                tableQuery.appendRow(0,tableQueryItem);
296                            }
297                    }
298                    
299                    DumpTable table = new DumpTable("#cccccc","#ffffff","#000000");
300                    table.setTitle("Debugging Output");
301                    table.setWidth("100%");
302                    table.appendRow(1,new SimpleDumpData("Pages"),boxPage);
303                    
304                    
305                    if(tableExceptions!=null && !tableExceptions.isEmpty())table.appendRow(1,new SimpleDumpData("Caught Exceptions"),tableExceptions);
306                    if(tableTimer!=null && !tableTimer.isEmpty())table.appendRow(1,new SimpleDumpData("Timers"),tableTimer);
307                    if(tableTraces!=null && !tableTraces.isEmpty())table.appendRow(1,new SimpleDumpData("Traces"),tableTraces);
308                    if(tableQuery!=null && !tableQuery.isEmpty())table.appendRow(1,new SimpleDumpData("Queries"),tableQuery);
309                    
310                    return table;
311            }
312    
313            private String getTemplate(Config config,PageException pe,boolean withLine) {
314                    try {
315                            Array arr = ((PageExceptionImpl)pe).getTagContext(config);
316                            Struct sct=Caster.toStruct(arr.getE(1));
317                            
318                            String templ= Caster.toString(sct.get(KeyImpl.TEMPLATE));
319                            if(withLine)templ +=":"+ Caster.toString(sct.get(KeyImpl.LINE));
320                            return templ;
321                    } 
322                    catch (Throwable t) {}
323                    
324                    return "";
325            }
326    
327            private String toString(Object o) {
328                    if(o==null) return "";
329                    return Caster.toString(o,"");
330            }
331    
332            private ArrayList<DebugEntry> toArray() {
333            ArrayList<DebugEntry> arrPages=new ArrayList<DebugEntry>(pages.size());
334            Iterator<String> it = pages.keySet().iterator();
335            while(it.hasNext()) {
336                DebugEntry page = (DebugEntry) pages.get(it.next());
337                page.resetQueryTime();
338                arrPages.add(page);
339                
340            }
341            
342            Collections.sort(arrPages,new DebugEntryComparator());
343            
344    
345            // Queries
346            int len=queries.size();
347            for(int i=0;i<len;i++) {
348                QueryEntry entry=(QueryEntry) queries.get(i);
349                String path=entry.getSrc();
350                Object o=pages.get(path);
351                
352                if(o!=null) {
353                    DebugEntry oe=(DebugEntry) o;
354                    oe.updateQueryTime(entry.getExe());
355                }
356            }
357            
358            return arrPages;
359        }
360    
361            private DumpData _toDumpData(int value) {
362            return new SimpleDumpData(_toString(value));
363        }
364            private String _toString(int value) {
365            if(value<=0) return "0";
366            return String.valueOf(value);
367        }
368     
369        /**
370         * @see railo.runtime.debug.Debugger#addQueryExecutionTime(java.lang.String, java.lang.String, railo.runtime.db.SQL, int, railo.runtime.PageSource, int)
371         */
372            // FUTURE set deprecated
373            public void addQueryExecutionTime(String datasource,String name,SQL sql, int recordcount, PageSource src,int time) {
374                    queries.add(new QueryEntryImpl(null,datasource,name,sql,recordcount,src.getDisplayPath(),time));
375            }
376            
377            // FUTURE add to interface
378            public void addQuery(Query query,String datasource,String name,SQL sql, int recordcount, PageSource src,int time) {
379                    queries.add(new QueryEntryImpl(query,datasource,name,sql,recordcount,src.getDisplayPath(),time));
380            }
381            
382            /**
383         * @see railo.runtime.debug.Debugger#setOutput(boolean)
384         */
385            public void setOutput(boolean output) {
386                    this.output = output;
387            }
388        
389        /**
390         * @see railo.runtime.debug.Debugger#getQueries()
391         */
392        public List<QueryEntryImpl> getQueries() {
393            return queries;
394        }
395    
396        /**
397         * @see railo.runtime.debug.Debugger#writeOut(railo.runtime.PageContext)
398         */
399        public void writeOut(PageContext pc) throws IOException {
400            //stop();
401            if(!output)return;
402            
403            String template=pc.getConfig().getDebugTemplate();
404                    
405            // no debug File 
406                    if(StringUtil.isEmpty(template)) {
407                        pc.forceWrite(pc.getConfig().getDefaultDumpWriter().toString(pc,toDumpData(pc, 9999,DumpUtil.toDumpProperties()),true)); 
408                        return;
409                    } 
410                    try {
411                        pc.doInclude(template);
412            } 
413                    catch (PageException e) {
414                pc.handlePageException(e);
415            }
416        }
417        
418        /**
419         * @see railo.runtime.debug.Debugger#getDebuggingData()
420         */
421        public Struct getDebuggingData() {
422                    List<QueryEntryImpl> queries = getQueries();
423                Struct qryExe=new StructImpl();
424                ListIterator<QueryEntryImpl> qryIt = queries.listIterator();
425            String[] cols = new String[]{"name","time","sql","src","count","datasource","usage"};
426            String[] types = new String[]{"VARCHAR","DOUBLE","VARCHAR","VARCHAR","DOUBLE","VARCHAR","ANY"};
427            
428            //queries
429            Query qryQueries=null;
430            try {
431                qryQueries = new QueryImpl(cols,types,queries.size(),"query");
432            } catch (DatabaseException e) {
433                qryQueries = new QueryImpl(cols,queries.size(),"query");
434            }
435                    int row=0;
436                    try {
437                        while(qryIt.hasNext()) {
438                            row++;
439                            QueryEntry qe=(QueryEntry) qryIt.next();
440                                    qryQueries.setAt(KeyImpl.NAME,row,qe.getName()==null?"":qe.getName());
441                            qryQueries.setAt(KeyImpl.TIME,row,Integer.valueOf(qe.getExe()));
442                            qryQueries.setAt(SQL,row,qe.getSQL().toString());
443                                    qryQueries.setAt(SRC,row,qe.getSrc());
444                    qryQueries.setAt(COUNT,row,Integer.valueOf(qe.getRecordcount()));
445                    qryQueries.setAt(DATASOURCE,row,qe.getDatasource());
446                    
447                    Struct usage = getUsage(qe);
448                    if(usage!=null) qryQueries.setAt(USAGE,row,usage);
449                    
450                    
451                    
452                            Object o=qryExe.get(KeyImpl.init(qe.getSrc()),null);
453                            if(o==null) qryExe.setEL(KeyImpl.init(qe.getSrc()),Integer.valueOf(qe.getExe()));
454                            else qryExe.setEL(KeyImpl.init(qe.getSrc()),Integer.valueOf(((Integer)o).intValue()+qe.getExe()));
455                        }
456                    }
457                    catch(PageException dbe) {}
458                    
459                // Pages
460                // src,load,app,query,total
461                Struct debugging=new StructImpl();
462                        
463                    row=0;
464            ArrayList<DebugEntry> arrPages = toArray();
465                    int len=arrPages.size();
466            Query qryPage=new QueryImpl(
467                    new String[]{"id","count","min","max","avg","app","load","query","total","src"},
468                    len,"query");
469    
470                    try {
471                DebugEntry de;
472                //PageSource ps;
473                        for(int i=0;i<len;i++) {
474                            row++;
475                            de=(DebugEntry) arrPages.get(i);
476                    //ps = de.getPageSource();
477                    
478                            qryPage.setAt("id",row,de.getId());
479                            qryPage.setAt("count",row,_toString(de.getCount()));
480                    qryPage.setAt("min",row,_toString(de.getMin()));
481                    qryPage.setAt("max",row,_toString(de.getMax()));
482                    qryPage.setAt("avg",row,_toString(de.getExeTime()/de.getCount()));
483                    qryPage.setAt("app",row,_toString(de.getExeTime()-de.getQueryTime()));
484                    qryPage.setAt("load",row,_toString(de.getFileLoadTime()));
485                            qryPage.setAt("query",row,_toString(de.getQueryTime()));
486                    qryPage.setAt(KeyImpl.TOTAL,row,_toString(de.getFileLoadTime()+de.getExeTime()));
487                            qryPage.setAt("src",row,de.getSrc());    
488                            }
489                    }
490                    catch(PageException dbe) {}
491    
492                    // exceptions
493                    len=exceptions==null?0:exceptions.size();
494                    
495            Array arrExceptions=new ArrayImpl();
496            if(len>0) {
497                            Iterator<CatchBlock> it = exceptions.iterator();
498                            row=0;
499                            while(it.hasNext()) {
500                                    arrExceptions.appendEL(it.next());  
501                            }
502                            
503            }
504    
505                    // timers
506                    len=timers==null?0:timers.size();
507            Query qryTimers=new QueryImpl(
508                    new String[]{"label","time","template"},
509                    len,"timers");
510            if(len>0) {
511                    try {
512                            Iterator<DebugTimerImpl> it = timers.iterator();
513                            DebugTimer timer;
514                            row=0;
515                            while(it.hasNext()) {
516                                    timer=(DebugTimer) it.next();
517                                    row++;
518                                    qryTimers.setAt(KeyImpl.LABEL,row,timer.getLabel()); 
519                                    qryTimers.setAt(KeyImpl.TEMPLATE,row,timer.getTemplate()); 
520                                    qryTimers.setAt(KeyImpl.TIME,row,Caster.toDouble(timer.getTime()));    
521                            }
522                            }
523                            catch(PageException dbe) {}
524            }
525    
526                    // traces
527                    len=traces==null?0:traces.size();
528            Query qryTraces=new QueryImpl(
529                    new String[]{"type","category","text","template","line","action","varname","varvalue","time"},
530                    len,"traces");
531            if(len>0) {
532                    try {
533                            Iterator<DebugTraceImpl> it = traces.iterator();
534                            DebugTraceImpl trace;
535                            row=0;
536                            while(it.hasNext()) {
537                                    trace= it.next();
538                                    row++;
539                                    qryTraces.setAt(KeyImpl.TYPE,row,LogUtil.toStringType(trace.getType(), "INFO"));  
540                                    if(!StringUtil.isEmpty(trace.getCategory()))qryTraces.setAt("category",row,trace.getCategory()); 
541                                    if(!StringUtil.isEmpty(trace.getText()))qryTraces.setAt("text",row,trace.getText()); 
542                                    if(!StringUtil.isEmpty(trace.getTemplate()))qryTraces.setAt(KeyImpl.TEMPLATE,row,trace.getTemplate()); 
543                                    if(trace.getLine()>0)qryTraces.setAt(KeyImpl.LINE,row,new Double(trace.getLine())); 
544                                    if(!StringUtil.isEmpty(trace.getAction()))qryTraces.setAt("action",row,trace.getAction()); 
545                                    if(!StringUtil.isEmpty(trace.getVarName()))qryTraces.setAt("varname",row,trace.getVarName()); 
546                                    if(!StringUtil.isEmpty(trace.getVarValue()))qryTraces.setAt("varvalue",row,trace.getVarValue()); 
547                                    qryTraces.setAt(KeyImpl.TIME,row,new Double(trace.getTime())); 
548                            }
549                            }
550                            catch(PageException dbe) {}
551            }
552                    
553            Query history=new QueryImpl(new String[]{},0,"history");
554            try {
555                            history.addColumn(KeyImpl.ID, historyId);
556                    history.addColumn("level", historyLevel);
557                    } catch (PageException e) {
558                    }
559            
560                    debugging.setEL(PAGES,qryPage);
561                    debugging.setEL(QUERIES,qryQueries);
562                    debugging.setEL(TIMERS,qryTimers);
563                    debugging.setEL(TRACES,qryTraces);
564                    debugging.setEL(HISTORY,history);
565                    debugging.setEL(KeyImpl.EXCEPTIONS,arrExceptions);
566                    //debugging.setEL(TRACE_OBJECTS,arrTO);
567                    return debugging;
568        }
569    
570        
571        
572        
573            private static Struct getUsage(QueryEntry qe) throws PageException {
574                    Query qry = ((QueryEntryImpl)qe).getQry();
575            
576            QueryColumn c;
577            DebugQueryColumn dqc;
578            outer:if(qry!=null) {
579                    Struct usage=null;
580                    String[] columnNames = qry.getColumns();
581                    Collection.Key colName;
582                    for(int i=0;i<columnNames.length;i++){
583                            colName=KeyImpl.init(columnNames[i]);
584                            c = qry.getColumn(colName);
585                            if(!(c instanceof DebugQueryColumn)) break outer;
586                            dqc=(DebugQueryColumn) c;
587                            if(usage==null) usage=new StructImpl();
588                            usage.setEL(colName, Caster.toBoolean(dqc.isUsed()));
589                    }
590                    return usage;
591            }
592            return null;
593            }
594            
595    
596            private static String getUsageList(QueryEntry qe) throws PageException  {
597                    Query qry = ((QueryEntryImpl)qe).getQry();
598            StringBuilder sb=new StringBuilder();
599            QueryColumn c;
600            DebugQueryColumn dqc;
601            outer:if(qry!=null) {
602                    String[] columnNames = qry.getColumns();
603                    Collection.Key colName;
604                    for(int i=0;i<columnNames.length;i++){
605                            colName=KeyImpl.init(columnNames[i]);
606                            c = qry.getColumn(colName);
607                            if(!(c instanceof DebugQueryColumn)) break outer;
608                            dqc=(DebugQueryColumn) c;
609                            if(!dqc.isUsed()){
610                                    if(sb.length()>0) sb.append(", ");
611                                    sb.append(colName.getString());
612                            }
613                    }
614            }
615            return sb.toString();
616            }
617    
618            /**
619             * @see railo.runtime.debug.Debugger#addTimer(java.lang.String, long, java.lang.String)
620             */
621            public DebugTimer addTimer(String label, long time, String template) {
622                    DebugTimerImpl t;
623                    timers.add(t=new DebugTimerImpl(label,time,template));
624                    return t;
625            }
626    
627            /**
628             * @see railo.runtime.debug.Debugger#addTrace(int, java.lang.String, java.lang.String, railo.runtime.PageSource, java.lang.String)
629             */
630            public DebugTrace addTrace(int type, String category, String text, PageSource page,String varName,String varValue) {
631                    
632                    long _lastTrace =(traces.isEmpty())?lastEntry: lastTrace;
633                    lastTrace = System.currentTimeMillis();
634            StackTraceElement[] _traces = new Exception("Stack trace").getStackTrace();
635                    String clazz=page.getFullClassName();
636                    int line=0;
637                    
638                    // line
639                    for(int i=0;i<_traces.length;i++) {
640                            StackTraceElement trace=_traces[i];
641                    if(trace.getClassName().startsWith(clazz)) {
642                            line=trace.getLineNumber();
643                            }
644                    }
645                    
646                    DebugTraceImpl t;
647                    traces.add(t=new DebugTraceImpl(type,category,text,page.getDisplayPath(),line,"",varName,varValue,lastTrace-_lastTrace));
648                    return t;
649            }
650            
651            public DebugTrace addTrace(int type, String category, String text, String template,int line,String action,String varName,String varValue) {
652                    
653                    long _lastTrace =(traces.isEmpty())?lastEntry: lastTrace;
654                    lastTrace = System.currentTimeMillis();
655            
656                    DebugTraceImpl t;
657                    traces.add(t=new DebugTraceImpl(type,category,text,template,line,action,varName,varValue,lastTrace-_lastTrace));
658                    return t;
659            }
660            
661            /**
662             *
663             * @see railo.runtime.debug.Debugger#getTraces()
664             */
665            public DebugTrace[] getTraces() {
666                    return traces.toArray(new DebugTrace[traces.size()]);
667            }
668            
669            // FUTURE add to interface
670            public void addException(Config config,PageException pe) {
671                    if(exceptions.size()>1000) return;
672                    try {
673                            exceptions.add(((PageExceptionImpl)pe).getCatchBlock(config));
674                    }
675                    catch(Throwable t){}
676            }
677            
678            
679            // FUTURE add to interface
680            public CatchBlock[] getExceptions() {
681                    return exceptions.toArray(new CatchBlock[exceptions.size()]);
682            }
683    
684            public static boolean debugQueryUsage(PageContext pageContext, Query query) {
685                    if(pageContext.getConfig().debug() && query instanceof QueryImpl) {
686                            if(((ConfigWebImpl)pageContext.getConfig()).getDebugShowQueryUsage()){
687                                    ((QueryImpl)query).enableShowQueryUsage();
688                                    return true;
689                            }
690                    }
691                    return false;
692            }
693    
694    }