Writing log to /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/tai-e.log
java.version: 17.0.11
java.version.date: 2024-04-16
java.runtime.version: 17.0.11+7-LTS-207
java.vendor: Oracle Corporation
java.vendor.version: null
os.name: Mac OS X
os.version: 26.1
os.arch: aarch64
Tai-e Version: 0.5.2-SNAPSHOT
Tai-e Commit: 3f3b802611bf6b8187abc1785e6c5d08a4cf5a4a
Writing analysis plan to /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/tai-e-plan.yml
WorldBuilder starts ...
Warning: main class was not given!
10231 classes with 100939 methods in the world
WorldBuilder finishes, elapsed time: 1.93s
ir-dumper starts ...
Dumping IR in /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/tir
28 classes in scope (APP) of class analyses
ir-dumper finishes, elapsed time: 0.03s
routerAnalysis starts ...
Router analysis started at: 2025-11-11 20:03:31
Router analysis completed at: 2025-11-11 20:03:31 (Duration: 4ms)
routerAnalysis finishes, elapsed time: 0.00s
beanAnalysis starts ...
beanAnalysis finishes, elapsed time: 0.00s
injectPointsAnalysis starts ...
injectPointsAnalysis finishes, elapsed time: 0.00s
aspectAnalysis starts ...
Starting AOP aspect analysis...
aspectAnalysis finishes, elapsed time: 0.00s
pta starts ...
Loading taint config from /Users/yuntsy/My/Projects/Java/WebAnalyzer/configs/taint-config.yml
Cannot find source method '<javax.servlet.ServletRequestWrapper: java.lang.String getParameter(java.lang.String)>'
Cannot find source method '<org.example.springtest.CommandExecClass: java.lang.String index(jakarta.servlet.http.HttpServletRequest)>'
Cannot find sink method '<org.springframework.web.client.RestTemplate: org.springframework.http.ResponseEntity exchange(java.lang.String,org.springframework.http.HttpMethod,org.springframework.http.HttpEntity,java.lang.Class,java.lang.Object[])>'
Cannot find sink method '<org.example.springtest.FieldInjection: java.lang.String sayInjection()>'
TaintConfig:
sinks:
- { method: "<java.sql.Statement: java.sql.ResultSet executeQuery(java.lang.String)>", index: "0" }
- { method: "<java.sql.Connection: java.sql.PreparedStatement prepareStatement(java.lang.String)>", index: "0" }
transfers:
- { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: "base", to: "result", type: "java.lang.String" }
- { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: "0", to: "result", type: "java.lang.String" }
- { method: "<java.lang.String: char[] toCharArray()>", from: "base", to: "result", type: "char[]" }
- { method: "<java.lang.String: void <init>(char[])>", from: "0", to: "base", type: "java.lang.String" }
- { method: "<java.lang.String: void getChars(int,int,char[],int)>", from: "base", to: "2", type: "char[]" }
- { method: "<java.lang.String: java.lang.String format(java.lang.String,java.lang.Object[])>", from: "1[*]", to: "result", type: "java.lang.String" }
- { method: "<java.lang.StringBuffer: void <init>(java.lang.String)>", from: "0", to: "base", type: "java.lang.StringBuffer" }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: "0", to: "base", type: "java.lang.StringBuffer" }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: "0", to: "result", type: "java.lang.StringBuffer" }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: "base", to: "result", type: "java.lang.StringBuffer" }
- { method: "<java.lang.StringBuffer: java.lang.String toString()>", from: "base", to: "result", type: "java.lang.String" }
- { method: "<java.lang.StringBuilder: void <init>(java.lang.String)>", from: "0", to: "base", type: "java.lang.StringBuilder" }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: "0", to: "base", type: "java.lang.StringBuilder" }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: "0", to: "result", type: "java.lang.StringBuilder" }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: "base", to: "result", type: "java.lang.StringBuilder" }
- { method: "<java.lang.StringBuilder: java.lang.String toString()>", from: "base", to: "result", type: "java.lang.String" }
callSiteMode: true
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String requestMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String defaultMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String getMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String postMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String putMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String deleteMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String patchMapping()>
Added entry point (with params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String pathVariable(java.lang.Long)>
Added entry point (with params): <org.example.springtarget.di.entry.controller.RestControllerEntry: java.lang.String requestParameter(java.lang.String)>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String requestMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String getMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String postMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String putMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String deleteMapping()>
Add entry point (empty params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String patchMapping()>
Added entry point (with params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String pathVariable(java.lang.Long)>
Added entry point (with params): <org.example.springtarget.di.entry.controller.ControllerEntry: java.lang.String requestParameter(java.lang.String)>
Add entry point (empty params): <org.example.springtarget.example.UserController: void createOrder()>
Added entry point (with params): <org.example.springtarget.example.UserController: void getUser(jakarta.servlet.http.HttpServletRequest)>
Add entry point (empty params): <org.example.springtarget.example.UserController: void getUserAndId()>
Add entry point (empty params): <org.example.springtarget.example.UserController: void getServiceBeanMessage()>
方法 <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> 匹配切点: bean(orderService)
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AROUND -> 目标方法: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)>
方法 <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> 匹配切点: @within(org.springframework.stereotype.Service)
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER -> 目标方法: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)>
方法 <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> 匹配切点: execution(* org.example.springtarget.example.OrderService.createOrder(..))
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: BEFORE -> 目标方法: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)>
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER_RETURNING -> 目标方法: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)>
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER_THROWING -> 目标方法: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)>
✓ 处理 AOP 调用边: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> -> <org.example.springtarget.example.LogAspect: java.lang.Object aroundAdvice(org.aspectj.lang.ProceedingJoinPoint)>, 通知类型: AROUND
✓ 处理 AOP 调用边: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> -> <org.example.springtarget.example.LogAspect: void afterAdvice(org.aspectj.lang.JoinPoint)>, 通知类型: AFTER
✓ 处理 AOP 调用边: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> -> <org.example.springtarget.example.LogAspect: void beforeAdvice(org.aspectj.lang.JoinPoint)>, 通知类型: BEFORE
✓ 处理 AOP 调用边: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> -> <org.example.springtarget.example.LogAspect: void afterReturningAdvice(java.lang.Object)>, 通知类型: AFTER_RETURNING
✓ 处理 AOP 调用边: <org.example.springtarget.example.OrderService: void createOrder(java.lang.String)> -> <org.example.springtarget.example.LogAspect: void afterThrowingAdvice(java.lang.Exception)>, 通知类型: AFTER_THROWING
方法 <org.example.springtarget.example.UserServiceImpl: java.lang.String getUser()> 匹配切点: @within(org.springframework.stereotype.Service)
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER -> 目标方法: <org.example.springtarget.example.UserServiceImpl: java.lang.String getUser()>
✓ 处理 AOP 调用边: <org.example.springtarget.example.UserServiceImpl: java.lang.String getUser()> -> <org.example.springtarget.example.LogAspect: void afterAdvice(org.aspectj.lang.JoinPoint)>, 通知类型: AFTER
方法 <org.example.springtarget.example.UserServiceAbstractImpl: java.lang.String getUser()> 匹配切点: @within(org.springframework.stereotype.Service)
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER -> 目标方法: <org.example.springtarget.example.UserServiceAbstractImpl: java.lang.String getUser()>
✓ 处理 AOP 调用边: <org.example.springtarget.example.UserServiceAbstractImpl: java.lang.String getUser()> -> <org.example.springtarget.example.LogAspect: void afterAdvice(org.aspectj.lang.JoinPoint)>, 通知类型: AFTER
方法 <org.example.springtarget.example.UserServiceImpl: java.lang.String getId()> 匹配切点: @within(org.springframework.stereotype.Service)
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER -> 目标方法: <org.example.springtarget.example.UserServiceImpl: java.lang.String getId()>
✓ 处理 AOP 调用边: <org.example.springtarget.example.UserServiceImpl: java.lang.String getId()> -> <org.example.springtarget.example.LogAspect: void afterAdvice(org.aspectj.lang.JoinPoint)>, 通知类型: AFTER
方法 <org.example.springtarget.di.beans.ServiceBeanImpl2: java.lang.String getMessage()> 匹配切点: @within(org.springframework.stereotype.Service)
织入切面: org.example.springtarget.example.LogAspect -> 通知类型: AFTER -> 目标方法: <org.example.springtarget.di.beans.ServiceBeanImpl2: java.lang.String getMessage()>
✓ 处理 AOP 调用边: <org.example.springtarget.di.beans.ServiceBeanImpl2: java.lang.String getMessage()> -> <org.example.springtarget.example.LogAspect: void afterAdvice(org.aspectj.lang.JoinPoint)>, 通知类型: AFTER
[Pointer analysis] elapsed time: 0.02s
Detected 0 taint flow(s):
TFGDumper starts ...
Source nodes:
Sink nodes:
Dumping /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/taint-flow-graph.dot
TFGDumper finishes, elapsed time: 0.00s
-------------- Pointer analysis statistics: --------------
#var pointers: 71 (insens) / 91 (sens)
#objects: 27 (insens) / 27 (sens)
#var points-to: 54 (insens) / 60 (sens)
#static field points-to: 0 (sens)
#instance field points-to: 8 (sens)
#array points-to: 0 (sens)
#reachable methods: 39 (insens) / 45 (sens)
#call graph edges: 11 (insens) / 11 (sens)
----------------------------------------
1
pta finishes, elapsed time: 0.13s
cg starts ...
Call graph has 39 reachable methods and 11 edges
Dumping call graph to /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/call-graph.dot
Dumping reachable methods to /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/reachable-methods.txt
Dumping call edges to /Users/yuntsy/My/Projects/Java/WebAnalyzer/output/call-edges.txt
cg finishes, elapsed time: 0.01s
Tai-e finishes, elapsed time: 2.19s
📝 Overall Description
I'm using Tai-e to analyze Spring AOP functionality.
Spring dynamically creates proxy classes at runtime to intercept the createOrder()method, resulting in the actual call sequence:
beforeAdvice()->createOrder()->afterAdvice().I've implemented a plugin to analyze this call sequence:
In the weaveAdvice()method, I create
AOPEdgeand add it usingsolver.addCallEdge().The analysis successfully enters
DefaultSolverand inprocessCallEdge,callGraph.addEdge(edge)returnstrueHowever, in the final call graph output, only the calls inside
beforeAdvice():But the expected AOP edges like:
are not present in the output. It seems like
callGraph.addEdge(edge)is not working properly forAOPEdges?🎯 Expected Behavior
Edges like these should be included in the call graph.
🐛 Current Behavior
Edges like these are not included in the call graph.
🔄 Reproducible Example
No response
⚙️ Tai-e Arguments
🔍 Click here to see Tai-e Options
🔍 Click here to see Tai-e Analysis Plan
📜 Tai-e Log
🔍 Click here to see Tai-e Log
ℹ️ Additional Information
No response