I was just looking through my CF-Talk emails, when Tom Chiverton dropped a bomb shell on my brain. Someone had asked how to find out which function was calling a given function. Sounds complicated right (to me it does, so back off!)? But Tom was all like, oh, that's mad easy, just thrown an error and catch it. What?!? It's that easy?
I, of course, had to immediately test this, and yes, it is that easy. First, I set up a method called StackTrace:
Launch code in new window » Download code as text file »
This code throws a custom error and catches it. The CFCATCH struct that is auto-generated by the CFTry/CFCatch block has the environmental context at the point the error was thrown. This includes the Stack Trace and the Tag Context. I wrap these up in a structure and append them to a REQUEST-scoped array.
Then, I created a few functions to test this out:
Launch code in new window » Download code as text file »
Then, I went ahead and tested it:
Launch code in new window » Download code as text file »
This gives us some great information. Take a look at the THIRD stack trace (the one produced by A()->B()->C()):
coldfusion.runtime.CustomException: This is thrown to gain access to the strack trace. at
coldfusion.tagext.lang.ThrowTag.doStartTag(ThrowTag.java:124) at
coldfusion.runtime.CfJspPage._emptyTag(CfJspPage.java:1915) at
cfstrack_trace2ecfm1605070124$funcSTACKTRACE.runFunction(D:\...\strack_trace.cfm:10) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:348) at
coldfusion.filter.SilentFilter.invoke(SilentFilter.java:47) at
coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:294) at
coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:258) at
coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:56) at
coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:211) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:173) at
coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:1807) at
cfstrack_trace2ecfm1605070124$funcC.runFunction(D:\...\strack_trace.cfm:54) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:348) at
coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:258) at
coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:56) at
coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:211) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:173) at
coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:1807) at
cfstrack_trace2ecfm1605070124$funcB.runFunction(D:\...\strack_trace.cfm:47) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:348) at
coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:258) at
coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:56) at
coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:211) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:173) at
coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:1807) at
cfstrack_trace2ecfm1605070124$funcA.runFunction(D:\...\strack_trace.cfm:38) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:348) at
coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:258) at
coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:56) at
coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:211) at
coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:173) at
coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:1807) at
cfstrack_trace2ecfm1605070124.runPage(D:\...\strack_trace.cfm:65) at
coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:152) at
coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:349) at
coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65) at
coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:210) at
coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:51) at
coldfusion.filter.PathFilter.invoke(PathFilter.java:86) at
coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:69) at
coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:52) at
coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28) at
coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38) at
coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38) at
coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22) at
coldfusion.filter.RequestThrottleFilter.invoke(RequestThrottleFilter.java:115) at
coldfusion.CfmServlet.service(CfmServlet.java:107) at
coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:78) at
jrun.servlet.FilterChain.doFilter(FilterChain.java:86) at
com.seefusion.Filter.doFilter(Filter.java:49) at
com.seefusion.SeeFusion.doFilter(SeeFusion.java:1432) at
jrun.servlet.FilterChain.doFilter(FilterChain.java:94) at
jrun.servlet.FilterChain.service(FilterChain.java:101) at
jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:91) at
jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42) at
jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:257) at
jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:541) at
jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:204) at
jrunx.scheduler.ThreadPool$DownstreamMetrics.invokeRunnable(ThreadPool.java:318) at
jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:426) at
jrunx.scheduler.ThreadPool$UpstreamMetrics.invokeRunnable(ThreadPool.java:264) at
jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
It's not the easiest thing in the world to read, but you can certainly tell what functions call what functions. Even easier would be to look at the TagContext, but I am not in the mood to take screen shots... just trust me on that one. Anyway, Tom, you the man. Awesome suggestion!
Download Code Snippet ZIP File
Comments (16) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
hmm, not sure if I understood your post correctly, but wouldn't targetted cfdumps work just the same, and probably less code?
I do a lot of my deguggin with a cfdump followed by a cfabort. Then a cut and paste into the next part of my code till I find it.
Everybody does it differently I guess, whatever works for you!
Posted by noname on Nov 16, 2006 at 10:30 AM
You could also just dump the cf catch in the cf catch block.
<cf catch>
<cf dump var="#cf catch#">
</cf catch>
That's assuming the original question was directed towards debugging.
Let's hope the original question wasn't based on an idea of trying to build a function that works differently depending on the caller. Someone used to working with the custom tags and functions like GetBaseTagList() might think along those lines.
Posted by Christopher Wigginton on Nov 16, 2006 at 10:31 AM
@Christopher,
Good point. The only issue is that would only work in code that allows output to the page. Anything inside of a CFSilent tag or an output="false" function would not allow for this.
But as the guy above states, there are lots of ways to do this, I just had never thought of this one (the one I posted about). Whatever works for you, works :)
Posted by Ben Nadel on Nov 16, 2006 at 10:38 AM
Ben, actually even if the code is inside cfsilent, etc., all you need to do is add a cfabort after the cfdump of the cfcatch object and you'll see it on the screen.
Posted by Brian Kotek on Nov 16, 2006 at 11:34 AM
Brian,
I am huge fan of the CFDump / CFAbort combo. The problem with it is that it haults the rest of the page. The beauty of CFTry/CFCatch solution is that you can execute the entire page and then get a full list of chained method calls / template executions.
Of course, this is only for use if you need the chained feature I suppose. If not, I whole heartedly stand behind the use of CFDump / CFAbort.
Posted by Ben Nadel on Nov 16, 2006 at 11:46 AM
Couldn't you just use cftrace?
Posted by Qasim Rasheed on Nov 16, 2006 at 3:48 PM
Qasim,
You could use CFTrace, however, it must log stuff and the user might not want to mess with the log files. Also, and I am not too familiar with CFTrace, but I think it outputs to the screen when it's called... where as this solution allows you to output to the screen at will.
Two valid solutions, two different solutions.
Posted by Ben Nadel on Nov 16, 2006 at 3:53 PM
Ben, nice work (and kudos to Tom). You shouldn't have to jump to the defence of it though, any aditional method of debugging can only help and shouldn't be disgarded so quickly for preffered means. There's usually a time and place where debugging methods you notmally rely on don't help much, any additional tools can only be a good thing!
Posted by Ryan Stewart on Nov 17, 2006 at 3:47 AM
Good Job Ben...
hmmm...now I wonder who sparked that thread....oh wait...that's me ;-)
Well I guess my ramblings had to spawn a blog post somewhere sometime ;-)
Cheers
Posted by Bryan Stevenson on Nov 17, 2006 at 7:32 PM
Ryan,
Most definately. The more tools in the toolbelt, the better the building.
Posted by Ben Nadel on Nov 17, 2006 at 9:47 PM
Bryan,
The more conversations we (as a community) get going, the more we are all gonna start learning :)
Posted by Ben Nadel on Nov 17, 2006 at 9:58 PM
Absolutely Ben!! ;-)
Posted by Bryan Stevenson on Nov 20, 2006 at 11:32 AM
Excellent post Ben. Just what I was looking for.
I have a piece of code in my application (it's a HUGE webapp) that is being called somewhere in the system and we just can not figure out what the hell is calling this function.
So I will most certainly be using this immediately!! :)
Shotto!
Posted by Paolo Broccardo on Apr 15, 2008 at 9:23 AM
@Paolo,
I think debugging is the perfect place for this.
Posted by Ben Nadel on Apr 15, 2008 at 9:25 AM
You can find a stack trace tool that will throw a complete stack trace of your entire request to an external file at http://ketanjetty.com/coldfusion/monitor/
Posted by Ketan Jetty on Nov 7, 2008 at 8:48 AM
@Ketan,
Does this help at all with the ability to determine at run-time, programmatically, the method that called the currently executing method?
Posted by Ben Nadel on Nov 7, 2008 at 9:06 AM