golang[ssa callgraph] 获取调用图实战

最近在拆分一个旧服务,需要从几十万行代码中,按业务功能拆分出对应代码,并部署新服务;然而,面对这种巨型服务,代码调用错综复杂,纯人力拆分需要耗费很多时间;基于此,这里借助golang自带callgraph调用图能力,帮我们找到需要拆出的代码;

package mainimport ("fmt""io/ioutil""path/filepath""sort""strings""github.com/pkg/errors""golang.org/x/tools/go/packages""golang.org/x/tools/go/ssa/ssautil""golang.org/x/tools/go/callgraph""golang.org/x/tools/go/pointer"
)// getProjectUsedCall 获取项目使用中的调用方法
func getProjectUsedCall(projectPath string) ([]string, error) {projectModule, err := parseProjectModule(projectPath)if err != nil {return nil, errors.Wrap(err, "parseProjectModule fail")}log.Debugf("projectModule: %+v", projectModule)callMap, err := parseProjectCallMap(projectPath)if err != nil {return nil, errors.Wrap(err, "parseProjectCallMap fail")}log.Debugf("callMap: %+v", callMap)srcCall := fmt.Sprintf("%v.main", projectModule)isDeleteEdgeFunc := func(caller, callee string) bool {// 非本项目调用if !strings.Contains(caller, projectModule) || !strings.Contains(callee, projectModule) {return true}// 非初始化调用if isInitCall(caller) || isInitCall(callee) {return true}// 非自我调用if caller == callee {return true}return false}// 过滤不需要的边for caller, callees := range callMap {for callee := range callees {if isDeleteEdgeFunc(caller, callee) {delete(callees, callee)}}if len(callees) == 0 {delete(callMap, caller)}}// 广度搜索图for {srcCallees := callMap[srcCall]srcSize := len(srcCallees)for srcCallee := range srcCallees {for nextCallee := range callMap[srcCallee] {callMap[srcCall][nextCallee] = true}}if srcSize == len(callMap[srcCall]) {break}}// 调用源涉及到的所有方法var callees []stringfor c := range callMap[srcCall] {callees = append(callees, c)}sort.Strings(callees)return callees, nil
}// parseProjectCallMap 解析项目调用图
func parseProjectCallMap(projectPath string) (map[string]map[string]bool, error) {projectModule, err := parseProjectModule(projectPath)if err != nil {return nil, errors.Wrap(err, "parseProjectModule fail")}log.Debugf("projectModule: %+v", projectModule)result, err := analyzeProject(projectPath)if err != nil {return nil, errors.Wrap(err, "analyzeProject fail")}log.Debugf("analyzeProject: %+v", result)// 遍历调用链路var callMap = make(map[string]map[string]bool)visitFunc := func(edge *callgraph.Edge) error {if edge == nil {return nil}// 解析调用者和被调用者caller, callee, err := parseCallEdge(edge)if err != nil {return errors.Wrap(err, "parseCallEdge fail")}// 记录调用关系if callMap[caller] == nil {callMap[caller] = make(map[string]bool)}callMap[caller][callee] = truereturn nil}err = callgraph.GraphVisitEdges(result.CallGraph, visitFunc)if err != nil {return nil, errors.Wrap(err, "GraphVisitEdges fail")}return callMap, nil
}func parseProjectModule(projectPath string) (string, error) {modFilename := filepath.Join(projectPath, "go.mod")content, err := ioutil.ReadFile(modFilename)if err != nil {return "", errors.Wrap(err, "ioutil.ReadFile fail")}lines := strings.Split(string(content), "\n")module := strings.TrimPrefix(lines[0], "module ")module = strings.TrimSpace(module)return module, nil
}func analyzeProject(projectPath string) (*pointer.Result, error) {// 生成Go Packagespkgs, err := packages.Load(&packages.Config{Mode: packages.LoadAllSyntax,Dir:  projectPath,})if err != nil {return nil, errors.Wrap(err, "packages.Load fail")}log.Debugf("pkgs: %+v", pkgs)// 生成ssa 构建编译prog, ssaPkgs := ssautil.AllPackages(pkgs, 0)prog.Build()log.Debugf("ssaPkgs: %+v", ssaPkgs)// 使用pointer生成调用链路return pointer.Analyze(&pointer.Config{Mains:          ssaPkgs,BuildCallGraph: true,})
}func parseCallEdge(edge *callgraph.Edge) (string, string, error) {const callArrow = "-->"edgeStr := fmt.Sprintf("%+v", edge)strArray := strings.Split(edgeStr, callArrow)if len(strArray) != 2 {return "", "", fmt.Errorf("invalid format: %v", edgeStr)}callerNodeStr, calleeNodeStr := strArray[0], strArray[1]caller, callee := getCallRoute(callerNodeStr), getCallRoute(calleeNodeStr)return caller, callee, nil
}func getCallRoute(nodeStr string) string {nodeStr = strings.TrimSpace(nodeStr)if strings.Contains(nodeStr, ":") {nodeStr = nodeStr[strings.Index(nodeStr, ":")+1:]}nodeStr = strings.ReplaceAll(nodeStr, "*", "")nodeStr = strings.ReplaceAll(nodeStr, "(", "")nodeStr = strings.ReplaceAll(nodeStr, ")", "")nodeStr = strings.ReplaceAll(nodeStr, "<", "")nodeStr = strings.ReplaceAll(nodeStr, ">", "")if strings.Contains(nodeStr, "$") {nodeStr = nodeStr[:strings.Index(nodeStr, "$")]}if strings.Contains(nodeStr, "#") {nodeStr = nodeStr[:strings.Index(nodeStr, "#")]}return strings.TrimSpace(nodeStr)
}func isInitCall(call string) bool {return strings.HasSuffix(call, ".init")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/152406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB实战应用案例精讲(二)-【图像处理】图像分类(附MATLAB代码实现)

目录 知识储备 图像分类、检测,语义分割 (1) 图像分类常用数据集 (2) 图像分类经典网络结构

原理Redis-Dict字典

Dict 1) Dict组成2) Dict的扩容3) Dict的收缩4) Dict的rehash5) 总结 1) Dict组成 Redis是一个键值型&#xff08;Key-Value Pair&#xff09;的数据库&#xff0c;可以根据键实现快速的增删改查。而键与值的映射关系正是通过Dict来实现的。 Dict由三部分组成&#xff0c;分别…

无重复最长字符串(最长无重复子字符串),剑指offer,力扣

目录 原题&#xff1a; 力扣地址&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 难度分析&#xff1a; 难度算中下吧&#xff0c;这个总体不算很难&#xff0c;而且滑动窗口&#xff0c;以及哈希都比较常见 审题目事例提示&#xff1a; 解题思路&#xff08;…

vue3 setup展示数据

效果图 1.创建数据 content.js import { reactive } from vueconst data reactive({color:red,title: 二十四节气,subTitle: 节气&#xff0c;是干支历中表示自然节律变化以及确立“十二月建”&#xff08;月令&#xff09;的特定节令。,list: [{name: "立春",con…

代码随想录-刷题第二天

977. 有序数组的平方 题目链接&#xff1a;977. 有序数组的平方 思路&#xff1a;双指针思想&#xff0c;数组是有序的且含有负数&#xff0c;其中元素的平方一定是两边最大。定义两个指针&#xff0c;从两端开始向中间靠近&#xff0c;每次比较两个指针的元素平方大小&#…

为什么创建百科词条?百科营销的作用

有些企业把百科营销看的很重&#xff0c;把企业、品牌、高管、产品等统统创建了几套百科词条&#xff0c;投资几万元。但也有的企业认为百科词条不带来流量&#xff0c;创建百度百科、360百科、头条百科或者维基百科等只是在做无用功&#xff0c;都没有什么价值&#xff0c;一个…

单元测试实战(二)Service 的测试

为鼓励单元测试&#xff0c;特分门别类示例各种组件的测试代码并进行解说&#xff0c;供开发人员参考。 本文中的测试均基于JUnit5。 单元测试实战&#xff08;一&#xff09;Controller 的测试 单元测试实战&#xff08;二&#xff09;Service 的测试 单元测试实战&#x…

电池管理系统设计与实现

目录 简介 1 、系统总体功能设计 2、 硬件设计 2.1 、硬件系统整体设计

Java 获取本地ip网卡信息

工具类 public static Optional<Inet4Address> getLocalIp4Address() throws SocketException {final List<Inet4Address> inet4Addresses getLocalIp4AddressFromNetworkInterface();if (inet4Addresses.size() ! 1) {final Optional<Inet4Address> ipBySo…

线上bug-接口速度慢

&#x1f47d;System.out.println(“&#x1f44b;&#x1f3fc;嗨&#xff0c;大家好&#xff0c;我是代码不会敲的小符&#xff0c;双非大四&#xff0c;Java实习中…”); &#x1f4da;System.out.println(“&#x1f388;如果文章中有错误的地方&#xff0c;恳请大家指正&a…

ClickHouse 物化视图

ClickHouse的物化视图是一种查询结果的持久化&#xff0c;它确实是给我们带来了查询效率的提升。用户查起来跟表没有区别&#xff0c;它就是一张表&#xff0c;它也像是一张时刻在预计算的表&#xff0c;创建的过程它是用了一个特殊引擎&#xff0c;加上后来 as select&#xf…

腾讯云助力港华能源上线“碳汭星云2.0”,推动能源行业绿色低碳转型

11月17日&#xff0c;港华能源与腾讯云联合打造的港华智慧能源生态平台“碳汭星云2.0”升级上线。依托双方的连接、大数据能力和行业深耕经验&#xff0c;该平台打破了园区“数据孤岛”&#xff0c;进一步提升了数据治理、应用集成和复制推广能力&#xff0c;未来有望以综合能源…

【小呆的力学笔记】有限元专题之循环对称结构有限元原理

文章目录 1. 循环对称问题的提出2. 循环对称条件2.1 节点位移的循环对称关系2.2 节点内力的循环对称关系 3. 在平衡方程中引入循环对称条件 1. 循环对称问题的提出 许多工程结构都是其中某一扇面的n次周向重复&#xff0c;也就是是周期循环对称结构。如果弹性体的几何形状、约…

【每日刷题——语音信号篇】

思考与练习 练习2.1 语音信号在产生的过程中&#xff0c;以及被感知的过程中&#xff0c;分别要经过人体的哪些器官&#xff1f; 1.产生过程&#xff1a; 肺部空气 → \rightarrow →冲击声带 → \rightarrow →通过声道&#xff08;可以调节&#xff09; → \rightarrow →…

IDEA自动注解设置(中文版)

IDEA自动注解设置 1、添加类自动注释 文件 - 设置 - 编辑器 - 文件和代码模板 - Include - File Header /** *description&#xff1a;TODO *author&#xff1a; ${USER} *create&#xff1a; ${DATE} ${TIME} */2、添加类方法自动注释 文件 - 设置 - 编辑器 - 实时模版 - …

沸点 | Ultipa 图数据库金融应用场景优秀案例首批入选,金融街论坛年会发布

为推进图数据库在金融行业的创新应用试点&#xff0c;近日&#xff0c;在2023金融街论坛年会“全球金融科技中心网络年会暨ZIBS北京论坛”上&#xff0c;北京前沿金融监管科技研究院发布了基于国际标准组织——国际关联数据基准委员会&#xff08;LDBC&#xff09;的《图数据库…

Unexpected WSL error

问题描述 启动 Docker Desktop 报错 Unexpected WSL error&#xff0c;报错完整信息如下&#xff1a; Docker Desktop - Unexpected WSL error An unexpected error was encountered while executing a WSL command, Commoncauses include access rights issues, which occur…

阿里云ack集群升级流程

最近一直在升级过期的ack 集群版本 从1.22升级到1.24.。 参考&#xff1a; 升级流程、方式及所需时间

AIGC ChatGPT4对Gbase数据库进行总结

ChatGPT4 用一个Prompt完成Gbase数据库的总结。 AIGC ChatGPT 职场案例 AI 绘画 与 短视频制作 PowerBI 商业智能 68集 数据库Mysql 8.0 54集 数据库Oracle 21C 142集 Office 2021实战应用 Python 数据分析实战&#xff0c; ETL Informatica 数据仓库案例实战 Excel 2021实操 …

微机原理_14

一、单项选择题(本大题共15小题,每小题3分,共45分。在每小题给出的四个备选项中,选出一个正确的答案。&#xff09; 1,下面寻址方式的操作数不在存储器中的是(&#xff09; A. 堆栈寻址 B. 寄存器间址 C.寄存器寻址 D. 直接寻址 2,条件转移指令JNE的条件是(&#xff09; A. CF…