跳到主要内容

单平台例子

信息

单平台特指一般光靠sibyl2就可以完成的功能,多平台指与全研发流程其他平台如何协作。

sibyl2 的核心目标是作为代码元信息提取层,将一些研发流程中可能需要的信息提取出来供其他环节使用。 这里列出部分使用场景供参考。 如果您有新的想法,欢迎向仓库提issue或PR。

提示

下面所有例子均用 sibyl client 编写,可能随着迭代api会有变化,以源码为准,CI会保证其始终可用。 源码可以通过下面链接找到:

clients 均由工具自动生成,故不同语言的api基本是一致的,可以直接往下阅读。

1、逻辑 diff

结合原生的 git diff,用几行代码就可以扩展出一个逻辑级别的 diff 信息。

affectedFileMap := map[string][]int{
"pkg/core/parser.go": {4, 89, 90, 91, 92, 93, 94, 95, 96},
"pkg/core/unit.go": {27, 28, 29},
}

for fileName, lineList := range affectedFileMap {
strLineList := make([]string, 0, len(lineList))
for _, each := range lineList {
strLineList = append(strLineList, strconv.Itoa(each))
}

// 依次按照行,查出这些行对应的引用链与方法信息
functionWithPaths, _, err := apiClient.BasicQueryApi.
ApiV1FuncctxGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
File(fileName).
Lines(strings.Join(strLineList, ",")).
Execute()
assert.Nil(t, err)
assert.NotEmpty(t, functionWithPaths)

// 此刻你即可查看这些被影响的方法信息
for _, eachFunc := range functionWithPaths {
core.Log.Infof("file %s hit func %s, ref: %d, refed: %d",
fileName, *eachFunc.Name, len(eachFunc.Calls), len(eachFunc.ReverseCalls))
}

// 也可以很方便查看这些方法的上下游影响
for _, eachFunc := range functionWithPaths {
chain, _, err := apiClient.SignatureQueryApi.
ApiV1SignatureFuncctxRchainGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
Signature(eachFunc.GetSignature()).
Depth(5).
Execute()
assert.Nil(t, err)
// chain is a tree-like object
// access it with dfs/bfs
if chain.ReverseCallChains != nil {
for _, each := range chain.ReverseCallChains.GetChildren() {
core.Log.Infof("cur: %v", each.Content)
for _, eachSub := range each.GetChildren() {
core.Log.Infof("cur: %v", eachSub.Content)
// continue ...
// eachSub.GetChildren()
}
}
}

// also a normal call chain
chain, _, err = apiClient.SignatureQueryApi.
ApiV1SignatureFuncctxChainGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
Signature(eachFunc.GetSignature()).
Depth(5).
Execute()
assert.Nil(t, err)
assert.NotNil(t, chain)
}

// 当然,如果你对此不满意,你也可以用原生的接口自己生成调用链图,或实现任何你希望的
for _, each := range functionWithPaths {
// 可以进一步查询出被引用方法的信息
// 继续下去,可以查询出整个引用链图
for _, eachCall := range each.Calls {
detail, _, err := apiClient.SignatureQueryApi.
ApiV1FuncWithSignatureGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
Signature(eachCall).
Execute()
assert.Nil(t, err)
core.Log.Infof("call: %v", detail)
}
}
}

2、全局的函数查询

在许多情况下我们在搜索时并不知道方法存在于哪个文件内。我们也提供了跨文件的查询方案,非常灵活。

functionWithPaths, _, err := apiClient.RegexQueryApi.
ApiV1FuncWithRegexGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
// 查询 name 字段中 符合下列正则匹配式的函数
Field("name").
Regex(".*Handle.*").
Execute()
assert.Nil(t, err)
assert.NotEmpty(t, functionWithPaths)

for _, each := range functionWithPaths {
assert.True(t, strings.Contains(*each.Name, "Handle"))
}

它可以匹配任何字段。例如,java中的注解。首先我们随便查一个java函数,可以看到它的结构:

{
"name": "closeIfPossible",
"receiver": "com.alibaba.jvm.sandbox.agent.SandboxClassLoader",
"parameters": null,
"returns": [
{
"type": "void",
"name": ""
}
],
"extras": {
"annotations": [
"@SuppressWarnings(\"unused\")"
],
"classInfo": {
"annotations": null,
"className": "SandboxClassLoader",
"packageName": "com.alibaba.jvm.sandbox.agent"
}
},
"lang": "JAVA",
"path": "sandbox-agent/src/main/java/com/alibaba/jvm/sandbox/agent/SandboxClassLoader.java",
"calls": [
"com.alibaba.jvm.sandbox.api.http.printer.Printer::close||void",
"com.alibaba.jvm.sandbox.agent.SandboxClassLoader::forceGetDeclaredFieldValue|Class<?>,String,Object|<T>",
"com.alibaba.jvm.sandbox.api.http.printer.ConcurrentLinkedQueuePrinter::close||void"
],
"reverseCalls": [
"com.alibaba.jvm.sandbox.core.manager.impl.DefaultCoreModuleManager::closeModuleJarClassLoaderIfNecessary|ClassLoader|void",
"com.alibaba.jvm.sandbox.agent.AgentLauncher::uninstall|String|void",
"com.alibaba.jvm.sandbox.core.manager.impl.ModuleJarLoader::load|ModuleLoadCallback|void"
]
}

可以发现他的注解是放在 extras.annotations 下的,那么:

functionWithPaths, _, err := apiClient.RegexQueryApi.
ApiV1FuncWithRegexGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
// 查询注解中包含 Test 字样的函数
Field("extras.annotations").
Regex(".*Test.*").
Execute()

其他字段只要利用同样的规则查询即可。 由于不同语言的extras字段各不相同,可以结合实际查出来的函数信息指定您的查询规则。

3、冷热函数查询

基于上一点我们可以全局搜索函数,那必然我们可以基于此做一些更高级的封装。例如查询被引用次数超过一定数量的函数?

fc, _, err := apiClient.ReferenceQueryApi.
ApiV1FuncctxWithReferencedCountGet(ctx).
Repo(projectName).
Rev(head.Hash().String()).
MoreThan(10).
LessThan(100).
Execute()
assert.Nil(t, err)
assert.NotEmpty(t, fc)

...

OpenSibyl 的唯一限制是想象力。非常欢迎贡献您的落地例子 :)