diff --git a/MagicEyes/CMakeLists.txt b/MagicEyes/CMakeLists.txt index 549a17dfa..948af1244 100644 --- a/MagicEyes/CMakeLists.txt +++ b/MagicEyes/CMakeLists.txt @@ -158,3 +158,4 @@ endif () add_subdirectory(src/backend) add_subdirectory(src/bridge) add_subdirectory(src/magic_eyes_cli) +add_subdirectory(src/visualization) diff --git a/MagicEyes/src/visualization/CMakeLists.txt b/MagicEyes/src/visualization/CMakeLists.txt index 280e3fa68..5083fdb93 100644 --- a/MagicEyes/src/visualization/CMakeLists.txt +++ b/MagicEyes/src/visualization/CMakeLists.txt @@ -3,17 +3,16 @@ cmake_minimum_required(VERSION 3.10) # 定义项目名称 project(DataVisual LANGUAGES NONE) +#目标目录,install/visualization +set(VISUALIZATION_INSTALL_DIR visualization) +# 定义变量 A +set(EBPF_PROMETHEUS ${CMAKE_CURRENT_SOURCE_DIR}/eBPF_prometheus) -# 定义工作目录变量 -set(WORK_DIR "${CMAKE_BINARY_DIR}/src/visualization") - -# 确保工作目录存在 -file(MAKE_DIRECTORY ${WORK_DIR}) # 直接执行命令:go mod tidy execute_process( COMMAND go mod tidy - WORKING_DIRECTORY ${WORK_DIR} # 设置工作目录为 ${CMAKE_BINARY_DIR}/data-visual + WORKING_DIRECTORY ${EBPF_PROMETHEUS} RESULT_VARIABLE GO_MOD_TIDY_RESULT OUTPUT_VARIABLE GO_MOD_TIDY_OUTPUT ERROR_VARIABLE GO_MOD_TIDY_ERROR @@ -24,8 +23,8 @@ endif() # 直接执行命令:go build execute_process( - COMMAND go build -o ${WORK_DIR}/data-visual main.go - WORKING_DIRECTORY ${WORK_DIR} + COMMAND go build -o ${EBPF_PROMETHEUS}/data-visual main.go + WORKING_DIRECTORY ${EBPF_PROMETHEUS} RESULT_VARIABLE GO_BUILD_RESULT OUTPUT_VARIABLE GO_BUILD_OUTPUT ERROR_VARIABLE GO_BUILD_ERROR @@ -33,25 +32,43 @@ execute_process( if(NOT GO_BUILD_RESULT EQUAL 0) message(FATAL_ERROR "Failed to build 'data-visual': ${GO_BUILD_ERROR}") endif() - +#将生成的data-visual可执行文件 安装到install/visualization目录下 +install(PROGRAMS ${EBPF_PROMETHEUS}/data-visual + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE + DESTINATION ${VISUALIZATION_INSTALL_DIR} +) +#将checker,collector,dao,prom_core安装到安装到install/visualization目录下 +install(DIRECTORY ${EBPF_PROMETHEUS}/checker + ${EBPF_PROMETHEUS}/dao + ${EBPF_PROMETHEUS}/prom_core + DESTINATION ${VISUALIZATION_INSTALL_DIR} +) # 创建目录并生成文件 -file(MAKE_DIRECTORY ${WORK_DIR}/.output/data) -file(WRITE ${WORK_DIR}/.output/data/offcpu_stack.txt "") +file(MAKE_DIRECTORY ${VISUALIZATION_INSTALL_DIR}/.output/data) +file(WRITE ${VISUALIZATION_INSTALL_DIR}/.output/data/offcpu_stack.txt "") -# 直接执行脚本:runimages.sh + +# 安装脚本文件 runimages.sh 到指定目录,并设置安装后的权限 +install(PROGRAMS ${EBPF_PROMETHEUS}/runimages.sh + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE + DESTINATION ${VISUALIZATION_INSTALL_DIR} +) +# 执行脚本:runimages.sh execute_process( COMMAND ./runimages.sh - WORKING_DIRECTORY ${WORK_DIR} - RESULT_VARIABLE RUN_IMAGES_RESULT - OUTPUT_VARIABLE RUN_IMAGES_OUTPUT - ERROR_VARIABLE RUN_IMAGES_ERROR + WORKING_DIRECTORY ${VISUALIZATION_INSTALL_DIR} + RESULT_VARIABLE RUNIMAGES_RESULT + OUTPUT_VARIABLE RUNIMAGES_OUTPUT + ERROR_VARIABLE RUNIMAGES_ERROR +) +# 将vis.sh安装到目标目录 +install(PROGRAMS ${EBPF_PROMETHEUS}/vis.sh + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE + DESTINATION ${VISUALIZATION_INSTALL_DIR} ) -if(NOT RUN_IMAGES_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to run 'runimages.sh': ${RUN_IMAGES_ERROR}") -endif() # 清理目标:可选 add_custom_target(clean_data - COMMAND ${CMAKE_COMMAND} -E rm -rf ${WORK_DIR}/dao/data.db ${WORK_DIR}/.output/data + COMMAND ${CMAKE_COMMAND} -E rm -rf ${EBPF_PROMETHEUS}/dao/data.db ${EBPF_PROMETHEUS}/r/.output/data COMMENT "Cleaning up generated data and files" ) diff --git a/MagicEyes/src/visualization/Makefile b/MagicEyes/src/visualization/Makefile deleted file mode 100644 index a0f985b7a..000000000 --- a/MagicEyes/src/visualization/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -Prometheus_image = prom/prometheus -all: - go mod tidy - go build -o data-visual ./main.go - @mkdir -p ./.output/data - @touch ./.output/data/offcpu_stack.txt - -start_service: - ./runimages.sh - -clean_data: - rm -rf ./dao/data.db ./.output/data diff --git a/MagicEyes/src/visualization/checker/check.go b/MagicEyes/src/visualization/checker/check.go deleted file mode 100644 index a1f67104a..000000000 --- a/MagicEyes/src/visualization/checker/check.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// 该文件用于对输入进行合法性检查以及对输入进行初步的处理。 - -package checker - -import ( - "fmt" - "github.com/urfave/cli/v2" - "log" - "os" - "regexp" - "strings" - "time" -) - -const ( - MaxFileSize int64 = 100 * 1024 * 1024 -) - -// 定义了一个名为 CollectCheck 的函数,用于检查和获取要收集的文件路径及其他参数 -func CollectCheck(ctx *cli.Context) (string, error) { - //if err := CheckArgs(ctx, 1, ConstExactArgs); err != nil { - // return "", err - //} - - // 从命令行上下文中获取第一个参数,即文件路径 - file := ctx.Args().Get(0) - - // 检查输入字符串是否有效 - if !IsInputStringValid(file) { - return "", fmt.Errorf("input:%s is invalid", file) - } - - // 检查文件是否存在 - exist, err := PathExist(file) - if err != nil { - return "", err - } - // 如果文件不存在,返回相应的错误信息 - if !exist { - return "", fmt.Errorf("file %s is not exist", file) - } - - // 获取完整的命令行参数,并将它们连接成一个字符串 - // fullcommand 是一个包含参数的字符串切片 - fullcommand := ctx.Args().Slice() - // 将字符串切片中的元素连接成一个字符串,fullcommand 是一个包含命令行参数的字符串切片," " 是连接各个参数时使用的分隔符 - full := strings.Join(fullcommand, " ") - - // 返回完整的命令行参数作为结果,以及 nil 表示没有错误 - return full, nil -} - -func IsInputStringValid(input string) bool { - if input != "" { - if isOk, _ := regexp.MatchString("^[a-zA-Z0-9/._-]*$", input); isOk { - return isOk - } - } - return false -} - -func PathExist(path string) (bool, error) { - fileInfo, err := os.Stat(path) - if err == nil { - if !fileInfo.IsDir() && fileInfo.Size() > MaxFileSize { - return true, fmt.Errorf("the size of %s exceeds"+ - " the maximum value which is %d", fileInfo.Name(), MaxFileSize) - } - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} - -// 定义了一个名为 CheckNormalError 的函数,用于检查并处理普通的错误 -func CheckNormalError(err error) { - // 如果 err 不为 nil,表示发生了错误 - if err != nil { - // 使用 log.Fatalln 打印错误信息并终止程序 - log.Fatalln(err) - } -} - -// 该函数接收一个字符串参数 content,用于判断是否符合 "proc" 命令的输出格式 -func IsProcOutput(content string) bool { - // 定义了一个包含正则表达式的字符串,用于匹配 "proc" 命令的输出格式。该正则表达式包含了多个条件,用 | 分隔 - pattern := `flag:\d+\s+pid:\d+\s+comm:\S+\s+offcpu_id|oncpu_time:\d+\s+offcpu_time|oncpu_time:\d+\s+oncpu_id|offcpu_id:\d+\s+oncpu_time|offcpu_time:\d+\s+time:[\d.]+` - // 将字符串正则表达式编译成一个正则表达式对象 re - re := regexp.MustCompile(pattern) - // 检查传入的 content 是否与正则表达式匹配,如果匹配则返回 true,否则返回 false - return re.MatchString(content) -} - -func CutunexceptedSpace(content string) string { - re := regexp.MustCompile(`\s*:\s*`) - result := re.ReplaceAllString(content, ":") - return result -} - -func ConvertTimeStamp(timestamp int64) string { - t := time.Unix(0, timestamp) - formattedTime := t.Format("15:04:05.000000") - return formattedTime -} -func Isinvalid(string2 string) bool { - pattern := `<.*>` - re := regexp.MustCompile(pattern) - return re.MatchString(string2) -} - -func IsTCPwatchFirst(string2 string) bool { - pattern := `^\s*SOCK\s*COMM\s*SEQ\s*ACK\s*MAC_TIME\s*IP_TIME\s*TCP_TIME\s*RX\s*$` - re := regexp.MustCompile(pattern) - return re.MatchString(string2) -} - -func IsTcpObjection(string2 string) bool { - pattern := `netwatch` - match, _ := regexp.MatchString(pattern, string2) - return match -} - -func IsProcimage(string2 string) bool { - pattern := `proc` - match, _ := regexp.MatchString(pattern, string2) - return match -} - -func InvalidTcpData(string2 string) bool { - pattern1 := `invalid` - pattern2 := `User-Agent` - match1, _ := regexp.MatchString(pattern1, string2) - match2, _ := regexp.MatchString(pattern2, string2) - if match2 || match1 { - return true - } - return false -} - -func Istmuxlineone(string2 string) bool { - is, _ := regexp.MatchString(`pid`, string2) - return is -} - -func Istmuxlinetwo(string2 string) bool { - is, _ := regexp.MatchString(`acq_time`, string2) - return is -} diff --git a/MagicEyes/src/visualization/collector/collect_output.go b/MagicEyes/src/visualization/collector/collect_output.go deleted file mode 100644 index 1542f001e..000000000 --- a/MagicEyes/src/visualization/collector/collect_output.go +++ /dev/null @@ -1,674 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// 该文件用于将收集到的信息进行进行规范化处理,实现重定向,并与Prometheus可视化逻辑进行交互。 - -package collector - -import ( - "bufio" - "visualization/checker" - "visualization/dao" - "visualization/prom_core" - "fmt" - "io" - "log" - "os" - "os/exec" - "os/signal" - "path/filepath" - "strconv" - "strings" - "sync" - "syscall" - - "github.com/urfave/cli/v2" -) - -const firstline = int(1) - -// 定义了一个结构体类型 Aservice -type Aservice struct { - // 服务的名称 - Name string - // 服务的描述 - Desc string - // NewInst 是一个函数类型的字段,用于创建服务实例 - // 当一个函数返回 interface{} 类型时,它实际上是在表示该函数可以返回任何类型的值。 - // 在使用这样的设计时,调用方可能需要使用类型断言来将 interface{} 类型的服务实例转换为具体的类型,以便进行后续的操作。 - NewInst func(ctx *cli.Context, opts ...interface{}) (interface{}, error) -} - -// 定义了一个名为 GlobalServices 的变量,它是一个结构体类型的值 -var GlobalServices = struct { - // 使用 sync.RWMutex 类型的嵌入字段,提供读写锁功能 - sync.RWMutex - // services 是一个 map,键是字符串类型,值是指向 Aservice 结构体的指针 - services map[string]*Aservice -}{} - -func AddAService(svc *Aservice) error { - GlobalServices.Lock() - defer GlobalServices.Unlock() - - if _, existed := GlobalServices.services[svc.Name]; existed { - return fmt.Errorf("service existed: %s", svc.Name) - } - - GlobalServices.services[svc.Name] = svc - - return nil -} - -// 定义一个名为 RunServices 的函数,接受一个回调函数作为参数 -func RunServices(fn func(nm string, svc *Aservice) error) error { - // 对全局服务列表进行加锁 - GlobalServices.Lock() - // 在函数结束时解锁,确保解锁操作一定会执行 - defer GlobalServices.Unlock() - - // 遍历全局服务列表中的服务 - // name 是服务的名称,service 是服务实例 - for name, service := range GlobalServices.services { - // 调用传入的回调函数,并传递服务名称和服务实例作为参数 - if err := fn(name, service); err != nil { - // 如果回调函数返回错误,立即返回该错误 - return err - } - } - // 如果遍历完所有服务都没有发生错误,返回 nil 表示成功 - return nil -} - -// 定义一个名为 collectCommand 的 cli.Command 类型变量 -var collectCommand = cli.Command{ - // 设置命令的名称 - Name: "collect", - // 设置命令的别名(可以使用 "c" 作为别名) - Aliases: []string{"c"}, - // 设置命令的用途描述 - Usage: "collect system data by eBPF", - // 设置命令执行时调用的处理函数(Action) - Action: simpleCollect, -} - -// init 函数在包被导入时自动执行 -func init() { - // 初始化全局服务列表的 services 字段,使用 make 创建一个空的 map - GlobalServices.services = make(map[string]*Aservice) - - // 创建并配置一个名为 "collectData" 的服务实例 - svc := Aservice{ - Name: "collectData", - Desc: "collect eBPF data", - NewInst: newCollectCmd, // 指定该服务的 NewInst 方法为 newCollectCmd - } - - // 将服务实例添加到全局服务列表 - if err := AddAService(&svc); err != nil { - log.Fatalf("Failed to load ... error:%s\n", err) - return - } - - // 创建并配置一个名为 "procCollectData" 的服务实例 - procSvc := Aservice{ - Name: "procCollectData", - Desc: "collect process eBPF data", - NewInst: newProcCmd, // 指定该服务的 NewInst 方法为 newProcCmd - } - - // 将服务实例添加到全局服务列表 - if err := AddAService(&procSvc); err != nil { - log.Fatalf("Failed to load ... error:%s\n", err) - return - } - - // 创建并配置一个名为 "tmuxCollectData" 的服务实例 - tmuxSvc := Aservice{ - Name: "tmuxCollectData", - Desc: "collect data from lock_image", - NewInst: newTmuxCmd, // 指定该服务的 NewInst 方法为 newTmuxCmd - } - - // 将服务实例添加到全局服务列表 - if err := AddAService(&tmuxSvc); err != nil { - log.Fatalf("Failed to load ... error:%s\n", err) - return - } -} - -func newCollectCmd(ctx *cli.Context, opts ...interface{}) (interface{}, error) { - return collectCommand, nil -} - -func newProcCmd(ctx *cli.Context, opts ...interface{}) (interface{}, error) { - return proc_imageCommand, nil -} - -func newTmuxCmd(ctx *cli.Context, opts ...interface{}) (interface{}, error) { - return tmux_command, nil -} - -// 定义了一个名为 BPF_name 的结构体 -type BPF_name struct { - // 结构体包含一个字段 Name,表示 BPF 名称 - Name string -} - -// 定义了一个名为 simpleCollect 的函数,用于执行简单的收集操作 -func simpleCollect(ctx *cli.Context) error { - // 调用 CollectCheck 函数,检查并获取完整的命令行参数 - full, err := checker.CollectCheck(ctx) - if err != nil { - // 如果出现错误,直接返回错误 - return err - } - - // 使用 strings.Fields 将完整的命令行参数拆分成字段,并取第一个字段作为路径 - path := strings.Fields(full)[0] - - // 使用 strings.Split 将路径按 "/" 分割成切片 - pathlist := strings.Split(path, "/") - - // 创建 BPF_name 结构体实例,并将其 Name 字段初始化为处理过的文件名,去除了文件名中的 ".py" 后缀,表示收集操作的名称 - n := BPF_name{Name: strings.ReplaceAll(pathlist[len(pathlist)-1], ".py", "")} - - // 调用 BPF_name 结构体的 Run 方法执行收集操作 - return n.Run(full) -} - -// 定义了一个名为 CheckFileType 的函数,用于检查文件类型并返回相应的命令字符串 -func CheckFileType(full string) (filename, specificcommand string) { - // 创建一个字符串切片,用于构建命令 - cmdSlice := make([]string, 0) - // 将 "sudo" 添加到命令切片 - cmdSlice = append(cmdSlice, "sudo") - // stdbuf -oL 表示将标准输出设置为行缓冲模式。这样可以使得输出更及时地显示在终端上,而不会等到缓冲区满或遇到换行符才刷新 - // 将 "stdbuf" 添加到命令切片 - cmdSlice = append(cmdSlice, "stdbuf") - // 将 "-oL" 添加到命令切片,这是为了调整输出缓冲方式 - cmdSlice = append(cmdSlice, "-oL") - // 使用 strings.Fields 将完整的命令行参数拆分成字段,并取第一个字段作为路径 - path := strings.Fields(full)[0] - // 将文件路径转换为小写 - lowercaseFilename := strings.ToLower(path) - // 获取文件名 - fn := filepath.Base(lowercaseFilename) - // 如果文件路径以 ".py" 结尾 - if strings.HasSuffix(lowercaseFilename, ".py") { - // 打印日志,表示尝试运行一个 Python 程序 - log.Println("Try to run a BCC program.") - // 将 "python3" 添加到命令切片 - cmdSlice = append(cmdSlice, "python3") - // 将 "-u" 添加到命令切片,表示无缓冲输出 - cmdSlice = append(cmdSlice, "-u") - // 将输入的命令添加到命令切片 - cmdSlice = append(cmdSlice, full) - // 使用空格连接命令切片,形成完整的命令字符串 - cmdStr := strings.Join(cmdSlice, " ") - // 返回构建好的命令字符串 - return fn, cmdStr - } else { - // 如果不是以 ".py" 结尾 - // 打印日志,表示尝试运行一个 eBPF 程序 - log.Println("Try to run a libbpf program.") - // 将 "-u" 添加到命令切片,表示无缓冲输出 - // cmdSlice = append(cmdSlice, "-u") - // 将文件路径添加到命令切片 - cmdSlice = append(cmdSlice, full) - // 使用空格连接命令切片,形成完整的命令字符串 - cmdStr := strings.Join(cmdSlice, " ") - // 返回构建好的命令字符串 - return fn, cmdStr - } -} - -// 定义了一个名为 Run 的方法,属于 BPF_name 结构体 -func (b *BPF_name) Run(full string) error { - // 检查文件类型,获取相应的命令字符串 - fn, cmdStr := CheckFileType(full) - // 创建一个执行外部命令的 Command 对象 - // -c 表示后面的参数是一个命令字符串,而不是一个可执行文件 - cmd := exec.Command("sh", "-c", cmdStr) - - // 设置 SysProcAttr,用于设置新创建的进程的属性 - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - - // 获取命令的标准输出管道 - stdout, err := cmd.StdoutPipe() - log.Println("full command is :", cmdStr) - if err != nil { - log.Println("get stdout failed:", err) - } - - // 启动一个 goroutine 监听系统信号 - go listenSystemSignals(cmd) - //go getStdout(stdout) - - // 创建一个用于传递 map 数据的通道 - mapchan := make(chan []map[string]interface{}, 2) - - // 启动一个 goroutine 用于重定向命令的标准输出 - go redirectStdout(fn, stdout, mapchan) - - // 创建一个指向 prom_core.MyMetrics 类型的指针 metricsobj, - // 并使用结构体字段初始化 BPFName 和 Sqlinited。 - metricsobj := &prom_core.MyMetrics{BPFName: b.Name, Sqlinited: false} - - // 创建一个指向 dao.Sqlobj 类型的指针 sqlobj, - // 并使用结构体字段初始化 Tablename。 - sqlobj := &dao.Sqlobj{Tablename: b.Name} - metricsobj.Sqlobj = sqlobj - - // 启动 MyMetrics 实例的服务 - go metricsobj.StartService() - - // 启动一个 goroutine 处理从重定向标准输出通道收到的数据 - go func() { - for { - select { - // 当从通道中接收到数据时 - case <-mapchan: - // 从通道中获取 map 切片,将其赋值给 MyMetrics 实例的 Maplist 字段 - metricsobj.Maplist = <-mapchan - log.Println(metricsobj.Maplist) - // 更新 MyMetrics 实例的数据 - metricsobj.UpdateData() - // 如果 SQL 已初始化,则更新 SQL 数据;否则,初始化 SQL - if metricsobj.Sqlinited { - metricsobj.UpdataSql(fn) - } else { - metricsobj.Initsql(fn) - } - // 从通道中接收第二次数据 - <-mapchan - default: - } - } - }() - - // 启动命令 - err = cmd.Start() - if err != nil { - log.Printf("cmd.Start() analysis service failed: %v", err) - os.Exit(-1) - } - - // 等待命令执行完毕 - err = cmd.Wait() - if err != nil { - log.Printf("cmd.Run() analysis failed with: %v", err) - os.Exit(-1) - } - - return nil -} - -// 定义了一个名为 listenSystemSignals 的函数,用于监听系统信号 -func listenSystemSignals(cmd *exec.Cmd) { - // 创建一个用于接收系统信号的通道 - signalChan := make(chan os.Signal, 1) - - // 向 signalChan 注册接收的系统信号类型,包括 Interrupt、Kill 和 SIGTERM - signal.Notify(signalChan, os.Interrupt, os.Kill, syscall.SIGTERM) - - // 无限循环,等待接收系统信号 - for { - select { - // 当从 signalChan 中接收到信号时 - case <-signalChan: - // 向指定进程组发送 SIGKILL 信号,结束进程 - _ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) - - // 退出当前程序,返回状态码 1 - os.Exit(1) - } - } -} - -// 定义了一个名为 redirectStdout 的函数,用于读取 stdout,并将其解析成 map 数据发送到指定通道 -func redirectStdout(fn string, stdout io.ReadCloser, mapchan chan []map[string]interface{}) { - // 用于存储解析后的 map 数据的切片 - var maps []map[string]interface{} - // 互斥锁,用于保护 maps 切片的并发写入 - var mu sync.Mutex - // 创建一个 bufio.Scanner 用于逐行读取 stdout 的内容 - scanner := bufio.NewScanner(stdout) - // 存储标题的字符串切片 - var titles []string - var rsc_titles []string - var sched_titles []string - var syscall_titles []string - var ulock_titles []string - var kt_titles []string - - // 行号计数器 - var line_number = 1 - // 命令索引,用于区分不同的命令 - var commandindex = 0 - - // RESOURCE(1)、SCHEDULE(2)、SYSCALL(3)、USERLOCK(4)、KEYTIME(5) - var data_type = 0 - var set_map = 0 - var enable_tgid = 0 - // 1 代表已创建表头 - data_map := [6]int{0, 0, 0, 0, 0, 0} - - rsc_pidheader := make(map[int][]string) - sched_pidheader := make(map[int][]string) - syscall_pidheader := make(map[int][]string) - ulock_pidheader := make(map[int][]string) - kt_pidheader := make(map[int][]string) - - var pid int - var err error - - // proc_image - // 判断程序名是否为 proc_image - if fn == "proc_image" { - for scanner.Scan() { - // 获取一行的文本内容 - line := strings.ReplaceAll(scanner.Text(), "|", "") - fields := strings.Fields(line) - - if fields[0] == "RESOURCE" || fields[0] == "SCHEDULE" || fields[0] == "SYSCALL" || fields[0] == "USERLOCK" || fields[0] == "KEYTIME" { - switch fields[0] { - case "RESOURCE": - data_type = 1 - case "SCHEDULE": - data_type = 2 - case "SYSCALL": - data_type = 3 - case "USERLOCK": - data_type = 4 - case "KEYTIME": - data_type = 5 - } - set_map = 1 - enable_tgid = 0 - } else if set_map == 1 { - // 对于表头行判断是否已经记录过表头,若没记录过则记录到相应的map中 - if data_map[data_type] == 0 { - if fields[1] == "PID" { - for _, value := range fields[2:] { - switch data_type { - case 1: - rsc_titles = append(rsc_titles, value) - case 2: - sched_titles = append(sched_titles, value) - case 3: - syscall_titles = append(syscall_titles, value) - case 4: - ulock_titles = append(ulock_titles, value) - case 5: - kt_titles = append(kt_titles, value) - } - } - } else { - for _, value := range fields[3:] { - switch data_type { - case 1: - rsc_titles = append(rsc_titles, value) - case 2: - sched_titles = append(sched_titles, value) - case 3: - syscall_titles = append(syscall_titles, value) - case 4: - ulock_titles = append(ulock_titles, value) - case 5: - kt_titles = append(kt_titles, value) - } - } - enable_tgid = 1 - } - data_map[data_type] = 1 - } - set_map = 0 - } else { - if enable_tgid == 0 { - // pid 为 fields[1] - pid, err = strconv.Atoi(fields[1]) - if err != nil { - // 处理转换错误 - fmt.Println("Error:", err) - return - } - } else if enable_tgid == 1 { - // pid 为 fields[2] - pid, err = strconv.Atoi(fields[2]) - if err != nil { - // 处理转换错误 - fmt.Println("Error:", err) - return - } - } - - switch data_type { - case 1: - if _, ok := rsc_pidheader[pid]; !ok { - if enable_tgid == 0 { - for _, title := range rsc_titles { - if enable_tgid == 0 { - rsc_pidheader[pid] = append(rsc_pidheader[pid], fmt.Sprintf("%s(r)(%s)", fields[1], title)) - } else if enable_tgid == 1 { - rsc_pidheader[pid] = append(rsc_pidheader[pid], fmt.Sprintf("%s_%s(r)(%s)", fields[1], fields[2], title)) - } - } - } - } - rsc_Map := make(map[string]interface{}) - mu.Lock() - rsc_header := rsc_pidheader[pid] - if enable_tgid == 0 { - for i, value := range fields[2:] { - rsc_Map[rsc_header[i]] = value - } - } else if enable_tgid == 1 { - for i, value := range fields[3:] { - rsc_Map[rsc_header[i]] = value - } - } - mu.Unlock() - mapchan <- []map[string]interface{}{rsc_Map} - case 2: - if _, ok := sched_pidheader[pid]; !ok { - for _, title := range sched_titles { - if enable_tgid == 0 { - sched_pidheader[pid] = append(sched_pidheader[pid], fmt.Sprintf("%s(S)(%s)", fields[1], title)) - } else if enable_tgid == 1 { - sched_pidheader[pid] = append(sched_pidheader[pid], fmt.Sprintf("%s_%s(S)(%s)", fields[1], fields[2], title)) - } - } - } - sched_Map := make(map[string]interface{}) - mu.Lock() - sched_header := sched_pidheader[pid] - if enable_tgid == 0 { - for i, value := range fields[2:] { - sched_Map[sched_header[i]] = value - } - } else if enable_tgid == 1 { - for i, value := range fields[3:] { - sched_Map[sched_header[i]] = value - } - } - mu.Unlock() - mapchan <- []map[string]interface{}{sched_Map} - case 3: - if _, ok := syscall_pidheader[pid]; !ok { - for _, title := range syscall_titles { - if enable_tgid == 0 { - syscall_pidheader[pid] = append(syscall_pidheader[pid], fmt.Sprintf("%s(s)(%s)", fields[1], title)) - } else if enable_tgid == 1 { - syscall_pidheader[pid] = append(syscall_pidheader[pid], fmt.Sprintf("%s_%s(s)(%s)", fields[1], fields[2], title)) - } - } - } - syscall_Map := make(map[string]interface{}) - mu.Lock() - syscall_header := syscall_pidheader[pid] - if enable_tgid == 0 { - for i, value := range fields[2:] { - syscall_Map[syscall_header[i]] = value - } - } else if enable_tgid == 1 { - for i, value := range fields[3:] { - syscall_Map[syscall_header[i]] = value - } - } - mu.Unlock() - mapchan <- []map[string]interface{}{syscall_Map} - case 4: - if _, ok := ulock_pidheader[pid]; !ok { - for _, title := range ulock_titles { - if enable_tgid == 0 { - ulock_pidheader[pid] = append(ulock_pidheader[pid], fmt.Sprintf("%s(l)(%s)", fields[1], title)) - } else if enable_tgid == 1 { - ulock_pidheader[pid] = append(ulock_pidheader[pid], fmt.Sprintf("%s_%s(l)(%s)", fields[1], fields[2], title)) - } - } - } - ulock_Map := make(map[string]interface{}) - mu.Lock() - ulock_header := ulock_pidheader[pid] - if enable_tgid == 0 { - for i, value := range fields[2:] { - ulock_Map[ulock_header[i]] = value - } - } else if enable_tgid == 1 { - for i, value := range fields[3:] { - ulock_Map[ulock_header[i]] = value - } - } - mu.Unlock() - mapchan <- []map[string]interface{}{ulock_Map} - case 5: - if _, ok := kt_pidheader[pid]; !ok { - for _, title := range kt_titles { - if enable_tgid == 0 { - kt_pidheader[pid] = append(kt_pidheader[pid], fmt.Sprintf("%s(k)(%s)", fields[1], title)) - } else if enable_tgid == 1 { - kt_pidheader[pid] = append(kt_pidheader[pid], fmt.Sprintf("%s_%s(k)(%s)", fields[1], fields[2], title)) - } - } - } - kt_Map := make(map[string]interface{}) - mu.Lock() - kt_header := kt_pidheader[pid] - if enable_tgid == 0 { - for i, value := range fields[2:] { - kt_Map[kt_header[i]] = value - } - } else if enable_tgid == 1 { - for i, value := range fields[3:] { - kt_Map[kt_header[i]] = value - } - } - mu.Unlock() - mapchan <- []map[string]interface{}{kt_Map} - } - } - // 行号递增 - line_number += 1 - } - } else { - // 常规方法提取方法 - // 逐行扫描 stdout - for scanner.Scan() { - // 获取一行的文本内容 - line := scanner.Text() - // 处理第一行,提取标题信息 - if line_number == firstline { - // log.Printf("Title:%s\n", line) - // 将字符串 line 按照空白字符进行分割,并返回一个切片 parms,其中包含了被空白字符分割的各个子字符串 - parms := strings.Fields(line) - // 遍历切片 parms 中的每个元素,并将元素的值赋给变量 value。在这里,使用了下划线 _ 表示我们对元素的索引不感兴趣,只关注元素的值 - for _, value := range parms { - // 根据标题字段的值是否为 "COMM" 确定命令索引,如果不等于,则将 commandindex 的值增加 1 - if strings.ToUpper(value) != "COMM" { - commandindex = commandindex + 1 - } - - // 创建一个新的空 map,其键是字符串类型,值是空接口类型 interface{}。这种设置允许 map 中的值可以是任何类型 - one_map := make(map[string]interface{}) - // 向 one_map 中添加一个键值对,其中键是字符串 value,值是 nil - one_map[value] = nil - // 将新创建的 one_map 添加到切片 maps 中。这样,maps 就成为一个包含了多个这样的 map 的切片 - maps = append(maps, one_map) - - // 将标题字段添加到 titles 切片中 - titles = append(titles, value) - } - } else { - // 使用 strings.Fields 函数将字符串 line 按照空白字符分割成多个字段,返回一个切片 parms。这个切片包含了一行文本中的各个字段 - parms := strings.Fields(line) - // 声明了一个新的字符串切片 special_parms,用于存储处理后的字段。这个切片将用于存储由原始字段组成的新的字段切片,以保证字段数量与标题数量一致 - var special_parms []string - - // 检查字段数量是否与标题数量一致 - if len(parms) != len(titles) { - // log.Printf("title number: %d, content number:%d", len(titles), len(parms)) - // 声明一个字符串变量 COMM,用于存储合并后的字段值 - var COMM string - // 遍历一行文本中的字段(这个遍历过程没看太懂) - for i, value := range parms { - // 检查字段是否在命令字段之前或者之后 - if i < commandindex-1 && i >= len(parms)-commandindex { - // 将特殊处理的字段值添加到 special_parms 切片中 - special_parms = append(special_parms, value) - // 如果当前字段是命令字段 - } else if i == commandindex-1 { - // 将当前字段的值赋给 COMM - COMM = value - // 如果当前字段在命令字段之前 - } else if i < len(parms)-commandindex { - // 将当前字段的值追加到 COMM,用空格分隔 - COMM = COMM + " " + value - // 将合并后的字段值添加到 special_parms 切片中 - special_parms = append(special_parms, COMM) - } - } - - // 创建新的 map,并将数据发送到通道 - newMap := make(map[string]interface{}) - mu.Lock() - // 遍历特殊处理后的字段值 - for i, value := range special_parms { - // 将字段值与标题对应,构建新的 map - newMap[titles[i]] = value - } - mu.Unlock() - // 将新创建的 map 发送到通道 mapchan - mapchan <- []map[string]interface{}{newMap} - // 如果字段数量与标题数量一致 - } else { - // 创建新的 map,并将数据发送到通道 - newMap := make(map[string]interface{}) - mu.Lock() - for i, value := range parms { - newMap[titles[i]] = value - } - mu.Unlock() - // 将新创建的 map 发送到通道 mapchan - mapchan <- []map[string]interface{}{newMap} - } - } - - // 行号递增 - line_number += 1 - } - } -} diff --git a/MagicEyes/src/visualization/collector/collect_proc_image.go b/MagicEyes/src/visualization/collector/collect_proc_image.go deleted file mode 100644 index f36383de1..000000000 --- a/MagicEyes/src/visualization/collector/collect_proc_image.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// 为proc_image所适配的collector,实现对proc_image输出格式的支持。 - -package collector - -import ( - "bufio" - "visualization/checker" - "visualization/dao" - "visualization/prom_core" - "io" - "log" - "os" - "os/exec" - "regexp" - "strings" - "syscall" - "time" - - "github.com/urfave/cli/v2" - "gopkg.in/yaml.v2" -) - -// Pro_Setting 定义了设置项,通过读取同目录下的tmux_proc_setting.yaml实现对基本信息的设置。 -type Proc_Setting struct { - Name string `yaml:"proc_name"` - Path string `yaml:"proc_path"` - Pid string `yaml:"proc_pid"` - Max_Records int `yaml:"proc_max_records"` -} - -// 定义了一个名为 proc_imageCommand 的 CLI 命令,用于执行特定的数据收集任务 -var proc_imageCommand = cli.Command{ - Name: "proc_image", - // 设置命令的别名,即用户可以使用 "pro" 作为缩写形式来调用相同的命令 - Aliases: []string{"pro"}, - Usage: "Special collect data out from proc_image", - // 设置命令执行时调用的函数为 procCollect - Action: procCollect, -} - -// Get_Setting 函数用于获取设置的信息,返回一个错误、命令字符串和最大记录数 -func Get_Setting(which string) (error, string, int) { - // 获取当前工作目录的绝对路径 - currentDir, _ := os.Getwd() - // 读取配置文件 tmux_proc_setting.yaml 的内容 - content, err := os.ReadFile(currentDir + "/collector/tmux_proc_setting.yaml") - // 如果读取配置文件时发生错误,输出错误信息并返回错误 - if err != nil { - log.Fatalf("Error reading file: %v", err) - return err, "", 0 - } - command := "" - maxrecords := 0 - if which == "proc" { - // 声明一个 Proc_Setting 类型的变量 setting,用于存储从配置文件解析得到的设置 - var setting Proc_Setting - // 使用 YAML 解码器将配置文件内容解析到 setting 变量中 - err = yaml.Unmarshal(content, &setting) - // 如果解码时发生错误,输出错误信息并返回错误 - if err != nil { - log.Fatalf("Error unmarshaling YAML :%v", err) - return err, "", 0 - } - - // 构造命令字符串,包括路径和进程 ID - command = setting.Path + " -p " + setting.Pid - // 获取最大记录数 - maxrecords = setting.Max_Records - } else if which == "tmux" { - var setting Tmux_Setting - err = yaml.Unmarshal(content, &setting) - if err != nil { - log.Fatalf("Error unmarshaling YAML :%v", err) - return err, "", 0 - } - - command = setting.Path + " -p " + setting.Pid - maxrecords = setting.Max_Records - } else { - log.Fatalf("select setting failed.") - } - return nil, command, maxrecords -} - -func procCollect(ctx *cli.Context) error { - // 调用 Get_Setting 函数获取 "proc" 类型的设置信息,其中 _ 表示占位符,因为 Get_Setting 返回三个值,但当前只关心 command - _, command, _ := Get_Setting("proc") - return ProcRun(command) -} - -// ProcRun 是收集器的主函数,通过goroutin的方式实现数据收集,重定向,与prom_core包实现通信。 -// 该函数接收一个字符串参数 command,表示要执行的命令 -func ProcRun(command string) error { - // 检查文件类型,并将检查后的命令字符串存储在 cmdStr 变量中 - _, cmdStr := CheckFileType(command) - // 创建一个表示将要执行的命令的对象,并将其赋值给 cmd 变量 - cmd := exec.Command("sh", "-c", cmdStr) - - // 设置进程组 ID - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - // 获取命令的标准输出管道 - stdout, err := cmd.StdoutPipe() - // 输出完整的执行命令,方便调试 - log.Println("full command is :", cmdStr) - if err != nil { - log.Println("get stdout failed:", err) - } - - // 启动一个 goroutine 监听系统信号 - go listenSystemSignals(cmd) - - // 创建一个带有缓冲区的通道,用于在 goroutine 之间传递 map 类型的数据 - mapchan := make(chan map[string]interface{}, 2) - - loc, _ := time.LoadLocation("Asia/Shanghai") - // 获取当前时间,并将其转换为浮点数格式 - currenttime := float64(time.Now().In(loc).UnixNano()) / 1e9 - - // 将命令字符串按 "/" 分割成字符串切片 - pathlist := strings.Split(command, "/") - // 使用正则表达式判断命令中是否包含 "proc" - is_proc, _ := regexp.MatchString(`proc`, pathlist[len(pathlist)-1]) - // 使用正则表达式判断命令中是否包含 "lifecycle" - is_lifecycle, _ := regexp.MatchString(`lifecycle`, pathlist[len(pathlist)-1]) - // 使用正则表达式判断命令中是否包含 "lock" - is_lock, _ := regexp.MatchString(`lock`, pathlist[len(pathlist)-1]) - - if is_proc || is_lifecycle { - log.Println("This is lifecycle") - _, _, maxrecords := Get_Setting("proc") - // 启动 goroutine,将命令的标准输出传递给 mapchan - go redirectProc(stdout, mapchan) - - // 创建 prom_core.ProcMetrics 类型的变量 procdata,用于存储从 "proc" 类型的命令中收集到的数据 - procdata := prom_core.ProcMetrics{Max_records: maxrecords, NowTime: currenttime} - // 创建 dao.Sqlobj 类型的变量 sqlobj,用于与数据库交互,设置表名为 "proc_image_data" - sqlobj := &dao.Sqlobj{Tablename: "proc_image_data"} - procdata.Sqlobj = sqlobj - // 启动 goroutine,初始化并运行 procdata 中的数据处理服务 - go procdata.BootProcService() - - // 启动匿名 goroutine,用于处理从 mapchan 接收到的数据并进行相应的操作,如更新数据库 - go func() { - for { - select { - case <-mapchan: - procdata.Getorigindata(mapchan) - if procdata.Sqlinted { - procdata.UpdateSql() - } else { - procdata.Initsql() - } - procdata.UpdateRecords() - <-mapchan - default: - } - } - }() - - } else if is_lock { - log.Println("This is lock_image.") - _, _, maxrecords := Get_Setting("tmux") - go redirectTmux(stdout, mapchan) - tmuxdata := prom_core.TmuxMetrics{Max_records: maxrecords, NowTime: currenttime} - sqlobj := &dao.Sqlobj{Tablename: "tmux_data"} - tmuxdata.Sqlobj = sqlobj - go tmuxdata.BootProcService() - go func() { - for { - select { - case <-mapchan: - tmuxdata.Getorigindata(mapchan) - if tmuxdata.Sqlinted { - tmuxdata.UpdateSql() - } else { - tmuxdata.Initsql() - } - tmuxdata.UpdateRecords() - <-mapchan - default: - } - } - }() - } - - // 启动命令,如果启动失败,则输出错误信息并退出程序 - err = cmd.Start() - if err != nil { - log.Printf("cmd.Start() analysis service failed: %v", err) - os.Exit(-1) - } - - // 等待命令执行完成,如果执行失败,则输出错误信息并退出程序 - err = cmd.Wait() - if err != nil { - log.Printf("cmd.Run() analysis failed with: %v", err) - os.Exit(-1) - } - - return nil -} - -// redirectProc 实现数据重定向 -// stdout 表示命令的标准输出流,mapchan 表示用于传递数据的通道 -func redirectProc(stdout io.ReadCloser, mapchan chan map[string]interface{}) { - // 声明一个 map 类型的变量 onemap,用于存储从命令的标准输出中解析出的数据 - var onemap map[string]interface{} - // 使用 bufio.NewScanner 创建一个扫描器,用于逐行扫描命令的标准输出 - scanner := bufio.NewScanner(stdout) - // 循环读取命令的标准输出的每一行 - for scanner.Scan() { - // 获取当前行的文本内容 - line := scanner.Text() - // 去除行中的不必要空格 - line = checker.CutunexceptedSpace(line) - // 判断当前行是否是 "proc" 命令的输出 - if checker.IsProcOutput(line) { - // 创建一个新的 map 对象,用于存储该行数据 - onemap = make(map[string]interface{}) - // 将当前行按空格分割成字符串切片,每个元素表示一组键值对 - parms := strings.Fields(line) - // 循环处理每个键值对 - for _, value := range parms { - // 将键值对按冒号分割成键和值 - parts := strings.Split(value, ":") - // 将键值对存储到 onemap 中 - onemap[parts[0]] = parts[1] - } - // log.Println(onemap) - // 将存储了当前行数据的 onemap 发送到 mapchan 通道中,以便后续处理 - mapchan <- onemap - // 如果不是 "proc" 命令的输出,则继续下一轮循环 - } else { - continue - } - } -} diff --git a/MagicEyes/src/visualization/collector/collect_tmux.go b/MagicEyes/src/visualization/collector/collect_tmux.go deleted file mode 100644 index 9808cadf4..000000000 --- a/MagicEyes/src/visualization/collector/collect_tmux.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// 为lock_image所做的适配的收集器 - -package collector - -import ( - "bufio" - "visualization/checker" - "github.com/urfave/cli/v2" - "io" - "strings" -) - -type Tmux_Setting struct { - Name string `yaml:"tmux_name"` - Path string `yaml:"tmux_path"` - Pid string `yaml:"tmux_pid"` - Max_Records int `yaml:"tmux_max_records"` -} - -var tmux_command = cli.Command{ - Name: "tmux", - Usage: "Special collect data out from lock_image", - Action: tmuxCollect, -} - -func tmuxCollect(ctx *cli.Context) error { - _, command, _ := Get_Setting("tmux") - return ProcRun(command) -} - -func redirectTmux(stdout io.ReadCloser, mapchan chan map[string]interface{}) { - controler := 0 - scanner := bufio.NewScanner(stdout) - onemap := make(map[string]interface{}) - - for scanner.Scan() { - line := scanner.Text() - line = checker.CutunexceptedSpace(line) - - if controler == 0 { - if checker.Istmuxlineone(line) { - parms := strings.Fields(line) - for _, value := range parms { - parts := strings.Split(value, ":") - onemap[parts[0]] = parts[1] - controler = 1 - } - } - } else { - if checker.Istmuxlinetwo(line) { - parms := strings.Fields(line) - for _, value := range parms { - parts := strings.Split(value, ":") - onemap[parts[0]] = parts[1] - controler = 0 - } - mapchan <- onemap - onemap = make(map[string]interface{}) - } - } - } -} diff --git a/MagicEyes/src/visualization/collector/tmux_proc_setting.yaml b/MagicEyes/src/visualization/collector/tmux_proc_setting.yaml deleted file mode 100644 index 11a77a3ce..000000000 --- a/MagicEyes/src/visualization/collector/tmux_proc_setting.yaml +++ /dev/null @@ -1,9 +0,0 @@ -proc_name: proc_image -proc_path: ~/tmp/proc_image/proc_image -proc_pid: 22896 -proc_max_records: 20 # 一次展示的最大数量 - -tmux_name: tmux -tmux_path: /root/lmp/eBPF_Supermarket/eBPF_proc_image/lock_image -tmux_pid: 20080 -tmux_max_records: 200 # 一次展示的最大数量 \ No newline at end of file diff --git a/MagicEyes/src/visualization/dao/data_to_sqlite.go b/MagicEyes/src/visualization/dao/data_to_sqlite.go deleted file mode 100644 index da14ccd1a..000000000 --- a/MagicEyes/src/visualization/dao/data_to_sqlite.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// 数据持久化的方式:通过将数据写入数据库实现数据持久化。 - -package dao - -import ( - "fmt" - "log" - "os" - "strconv" - "strings" - - "gorm.io/driver/sqlite" - "gorm.io/gorm" -) - -var GlobalMap map[int][6]bool - -// 定义一个名为 Sqlobj 的结构体类型,用于封装数据库相关的信息 -type Sqlobj struct { - // Tablename 字段存储数据库表的名称。 - Tablename string - // db 字段是一个指向 gorm.DB 类型的指针,用于处理与数据库交互的对象。 - db *gorm.DB - // Data 字段是一个 map,存储与数据库相关的信息。 - Data map[string]interface{} -} - -type Basicdata struct { - ID int `gorm:"primaryKey;unique;column:ID"` -} - -// Connectsql 连接数据库 -func (s *Sqlobj) Connectsql() { - currentdir, _ := os.Getwd() - path := currentdir + "/dao/data.db" - db, _ := gorm.Open(sqlite.Open(path), &gorm.Config{}) - log.Println("connected.") - s.db = db -} - -func (s *Sqlobj) Tableexist(name string) bool { - return s.db.Migrator().HasTable(name) -} - -// CreateTable 建表 -func (s *Sqlobj) OperateTable(name string, fn string) { - // 检查表是否存在 - if !s.Tableexist(name) { - // 如果表不存在,先删除已存在的同名表 - deletetable := fmt.Sprintf("drop table if exists %s;", s.Tablename) - // 执行SQL语句,删除表 - if err := s.db.Exec(deletetable).Error; err != nil { - log.Fatalf("drop exist table failed.") - } - // 创建表 - if err := s.db.Table(s.Tablename).AutoMigrate(&Basicdata{}); err != nil { - log.Fatalf("create table failed.") - } - // 添加表 - if fn == "proc_image" { - s.ProcAppendTable() - } else { - s.AppendTable() - } - // 创建行 - s.CreateRow() - } else { - // 如果表存在,直接创建行 - s.CreateRow() - } -} - -// AppendTable 扩展表 -func (s *Sqlobj) AppendTable() { - for key, value := range s.Data { - datatype := "text" - if strvalue, is_string := value.(string); is_string { - // shift numerical data to float64 - if _, err := strconv.ParseFloat(strvalue, 64); err == nil { - datatype = "real" - } - } - addcolumn := fmt.Sprintf("alter table %s add column \"%s\" %s", s.Tablename, key, datatype) - s.db.Exec(addcolumn) - } -} - -func (s *Sqlobj) ProcAppendTable() { - enable := false - data := 0 - // 遍历数据集合 - for key, value := range s.Data { - if !enable { - var pid int - index := strings.Index(key, "(") - intPart := key[:index] - parts := strings.Split(intPart, "_") - if len(parts) >= 2 { - pid, _ = strconv.Atoi(parts[1]) - } else { - pid, _ = strconv.Atoi(intPart) - } - leftIndex := strings.Index(key, "(") - rightIndex := strings.Index(key, ")") - if leftIndex != -1 && rightIndex != -1 && rightIndex > leftIndex { - substring := key[leftIndex+1 : rightIndex] - switch substring { - case "r": - data = 1 - case "S": - data = 2 - case "s": - data = 3 - case "l": - data = 4 - case "k": - data = 5 - } - } - - if _, ok := GlobalMap[pid]; !ok { - GlobalMap[pid] = [6]bool{false, false, false, false, false, false} - array := GlobalMap[pid] - array[data] = true - GlobalMap[pid] = array - } else { - array := GlobalMap[pid] - if array[data] { - break - } - array[data] = true - GlobalMap[pid] = array - } - enable = true - } - // 默认数据类型为"text" - datatype := "text" - // 检查值是否为字符串类型 - if strvalue, is_string := value.(string); is_string { - // 如果值为字符串类型,则尝试将其转换为浮点数 - if _, err := strconv.ParseFloat(strvalue, 64); err == nil { - // 如果可以成功转换,则将数据类型设置为"real" - datatype = "real" - } - } - // 构建SQL语句,用于向表中添加列 - addcolumn := fmt.Sprintf("alter table %s add column \"%s\" %s", s.Tablename, key, datatype) - // 执行SQL语句,向表中添加列 - s.db.Exec(addcolumn) - } -} - -// CreateRow 写入数据 -func (s *Sqlobj) CreateRow() { - // 使用数据库连接对象创建行,并将数据插入指定表中 - s.db.Table(s.Tablename).Create(s.Data) -} - -func init() { - GlobalMap = make(map[int][6]bool) -} diff --git a/MagicEyes/src/visualization/data-visual b/MagicEyes/src/visualization/data-visual deleted file mode 100755 index 7e2f510d9..000000000 Binary files a/MagicEyes/src/visualization/data-visual and /dev/null differ diff --git a/MagicEyes/src/visualization/eBPF_prometheus b/MagicEyes/src/visualization/eBPF_prometheus new file mode 120000 index 000000000..d2ba976bf --- /dev/null +++ b/MagicEyes/src/visualization/eBPF_prometheus @@ -0,0 +1 @@ +../../../eBPF_Visualization/eBPF_prometheus/ \ No newline at end of file diff --git a/MagicEyes/src/visualization/go.mod b/MagicEyes/src/visualization/go.mod deleted file mode 100644 index 60d41a6b4..000000000 --- a/MagicEyes/src/visualization/go.mod +++ /dev/null @@ -1,31 +0,0 @@ -module visualization - -go 1.19 - -require ( - github.com/prometheus/client_golang v1.20.5 - github.com/urfave/cli/v2 v2.25.7 - gopkg.in/yaml.v2 v2.4.0 - gorm.io/driver/sqlite v1.5.7 - gorm.io/gorm v1.25.12 -) - -require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.17.9 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect -) diff --git a/MagicEyes/src/visualization/go.sum b/MagicEyes/src/visualization/go.sum deleted file mode 100644 index 0c2614880..000000000 --- a/MagicEyes/src/visualization/go.sum +++ /dev/null @@ -1,51 +0,0 @@ -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= -gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= -gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/MagicEyes/src/visualization/main.go b/MagicEyes/src/visualization/main.go deleted file mode 100644 index 815811865..000000000 --- a/MagicEyes/src/visualization/main.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// 主函数 - -// 声明这个文件属于 main 包,是一个可执行的程序 -package main - -// 导入所需的包,包括自定义的 checker 和 collector 包,以及一些标准库和第三方库 -import ( - "visualization/checker" - "visualization/collector" - "fmt" - "log" - "os" - "sort" - - "github.com/urfave/cli/v2" -) - -// 主函数的开始 -func main() { - // 创建一个新的 CLI 应用 - // cli是一个简单、快速、有趣的包,用于在Go中构建命令行应用程序。其目标是使开发人员能够以一种富有表现力的方式编写快速且可分发的命令行应用程序 - app := cli.NewApp() - // 配置应用的名称和使用说明文本 - app.Name = "data-visual" - app.Usage = ` use this cli-tool to collect output data and Convert output data to standard prometheus data. - here are two different sub-command : collect & ecli - example: - sudo data-visual collect ./vfsstat.py - sudo data-visual proc_image -` - // 运行 collector 包中的 RunServices 函数,该函数接受一个匿名函数作为回调 - // 该匿名函数将每个服务的实例转换为 cli.Command 接口,并将其添加到应用的命令列表中 - err := collector.RunServices(func(nm string, svc *collector.Aservice) error { - // 通过服务注册的 NewInst 函数创建服务实例 - ins, err := svc.NewInst(nil) - if err != nil { - return err - } - // 将 ins 转换为 cli.Command 接口 - cmd, ok := ins.(cli.Command) - if !ok { - fmt.Printf("service %s doesn't implement cli.Command\n", nm) - return fmt.Errorf("service %s doesn't implement cli.Command\n", nm) - } - // 将成功转换的命令实例 cmd 添加到应用的命令列表中 - app.Commands = append(app.Commands, &cmd) - return nil - }) - // 对应用的命令列表按照名称排序 - sort.Sort(cli.CommandsByName(app.Commands)) - - // 设置应用的 Before 钩子,该钩子将在执行命令之前运行 - // Before 钩子函数用于在执行应用程序的命令之前执行一些特定的任务 - app.Before = doBeforeJob - // 运行 CLI 应用,处理命令行参数,并在执行期间处理错误 - err = app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -// doBeforeJob 函数是应用的 Before 钩子函数,用于在执行命令之前执行一些操作,这里检查并处理错误 -func doBeforeJob(ctx *cli.Context) (err error) { - // 调用 checker 包中的 CheckNormalError 函数检查错误 - checker.CheckNormalError(err) - return nil -} diff --git a/MagicEyes/src/visualization/prom_core/processer.go b/MagicEyes/src/visualization/prom_core/processer.go deleted file mode 100644 index a7d0398b8..000000000 --- a/MagicEyes/src/visualization/prom_core/processer.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// Prometheus可视化的核心逻辑,实现将规范化的数据加载到Prometheus的metrics中,并启动http服务,供Prometheus-Service提取。 - -package prom_core - -import ( - "visualization/checker" - "visualization/dao" - "log" - "net/http" - "strconv" - "strings" - "sync" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" -) - -// 定义一个名为 MyMetrics 的结构体类型。 -type MyMetrics struct { - // BPFName 字段存储与此度量相关的 BPF 的名称。 - BPFName string - // mu 字段是一个互斥锁,用于在多协程之间同步对结构体字段的访问。 - mu sync.Mutex - // Maps 字段是一个 map,存储与此度量相关的信息。 - Maps map[string]interface{} - // Maplist 字段是一个切片,存储与此度量相关的信息的列表。 - Maplist []map[string]interface{} - // Sqlobj 字段是一个指向 dao.Sqlobj 类型的指针,用于处理与数据库相关的信息。 - Sqlobj *dao.Sqlobj - // Sqlinited 字段表示与此度量相关的数据库是否已初始化。 - Sqlinited bool -} - -func (m *MyMetrics) Describe(ch chan<- *prometheus.Desc) {} - -// Convert_Maps_To_Dict shift dict list to dict -func (m *MyMetrics) UpdateData() { - new_Dict := make(map[string]interface{}) - for _, dict := range m.Maplist { - for key, value := range dict { - new_Dict[key] = value - } - } - m.Maps = new_Dict -} - -func (m *MyMetrics) UpdataSql(fn string) { - m.Sqlobj.Data = m.Maps - if fn == "proc_image" { - m.Sqlobj.ProcAppendTable() - } - m.Sqlobj.CreateRow() -} - -func (m *MyMetrics) Initsql(fn string) { - m.Sqlobj.Data = m.Maps - m.Sqlobj.Connectsql() - m.Sqlobj.OperateTable(m.BPFName, fn) - m.Sqlinited = true -} - -// Format_Dict format dict. -func Format_Dict(dict map[string]interface{}) (map[string]float64, map[string]string) { - measurable_dict := map[string]float64{} - string_dict := map[string]string{} - for key, value := range dict { - if strvalue, is_string := value.(string); is_string { - // shift numerical data to float64 - if floatValue, err := strconv.ParseFloat(strvalue, 64); err == nil { - measurable_dict[key] = floatValue - } else { - if checker.Isinvalid(key) || strings.ToUpper(key) == "TIME" || strings.ToUpper(key) == "SOCK" { - continue - } - string_dict[key] = value.(string) - } - } - } - return measurable_dict, string_dict -} - -// Collect func collect data and load to metrics. -func (m *MyMetrics) Collect(ch chan<- prometheus.Metric) { - bpfdata, stringdata := Format_Dict(m.Maps) - for key, value := range bpfdata { - ch <- prometheus.MustNewConstMetric( - prometheus.NewDesc( - "bpf_metrics", - "collect data and load to metrics", - []string{"bpf_out_data"}, - stringdata, - ), - prometheus.GaugeValue, - value, - key, - ) - } -} - -// StartService 方法是 MyMetrics 类型的一个方法,用于启动服务并将 MyMetrics 注册到 Prometheus。 -func (m *MyMetrics) StartService() { - // 使用 Prometheus 的 MustRegister 函数将 MyMetrics 注册到 Prometheus 收集器中。 - prometheus.MustRegister(m) - - // 将 /metrics 路径映射到 Prometheus HTTP 处理器,以便可以通过该路径访问指标数据。 - http.Handle("/metrics", promhttp.Handler()) - - // 启动 HTTP 服务器,监听端口 8090,处理器为 nil(使用默认的多路复用器)。 - // 如果启动失败,使用 log.Fatalf 输出错误信息并终止程序。 - if err := http.ListenAndServe(":8090", nil); err != nil { - log.Fatalf("Failed to start HTTP server:", err) - } -} diff --git a/MagicEyes/src/visualization/prom_core/prometheus.yaml b/MagicEyes/src/visualization/prom_core/prometheus.yaml deleted file mode 100644 index c3ddc8f76..000000000 --- a/MagicEyes/src/visualization/prom_core/prometheus.yaml +++ /dev/null @@ -1,16 +0,0 @@ -#全局配置部分,应用于所有的 job 配置 -global: - # 设置全局的抓取间隔,即 Prometheus 将每隔 50 毫秒收集一次指标 - scrape_interval: 50ms - -# 抓取配置部分,用于指定要抓取的目标和相应的设置 -scrape_configs: - # 定义一个 job,命名为 'bpf_collector',这个 job 用于配置 Prometheus 如何抓取数据 - - job_name: 'bpf_collector' - # 指定从目标获取指标的路径,即采集路径 - metrics_path: '/metrics' - # 配置静态目标的部分,其中的目标地址是静态的,不会动态变化 - static_configs: - #需要采集的地址 - - targets: ['172.17.0.1:8090'] - #- targets: ['127.0.0.1:8090'] diff --git a/MagicEyes/src/visualization/prom_core/supplyforProc_image.go b/MagicEyes/src/visualization/prom_core/supplyforProc_image.go deleted file mode 100644 index 43cb5585b..000000000 --- a/MagicEyes/src/visualization/prom_core/supplyforProc_image.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// proc_image数据可视化处理的核心逻辑 - -package prom_core - -import ( - "visualization/dao" - "encoding/json" - "log" - "net/http" - "strconv" - "sync" -) - -// 定义单条记录的数据结构 -type ProcMetrics struct { - Max_records int - NowTime float64 - mu sync.Mutex - OriginalValue map[string]interface{} - Records []OneRecord - Sqlinted bool - Sqlobj *dao.Sqlobj -} - -// 原始数据处理后所保留的基本数据 -type OneRecord struct { - TimeStamp float64 `json:"timestamp"` - State string `json:"state"` - Durtion float64 `json:"durtion"` -} - -// Getorigindata 实现通信,获取原始数据 -func (p *ProcMetrics) Getorigindata(originalvalue chan map[string]interface{}) { - p.OriginalValue = <-originalvalue -} - -// 数据库操作 -func (p *ProcMetrics) UpdateSql() { - p.Sqlobj.Data = p.OriginalValue - p.Sqlobj.CreateRow() -} - -func (p *ProcMetrics) Initsql() { - p.Sqlobj.Data = p.OriginalValue - p.Sqlobj.Connectsql() - p.Sqlobj.OperateTable("proc_image", "") - p.Sqlinted = true -} - -// processJson 实现将原始数据进行处理,获取展示所需要的基本数据 -func (p *ProcMetrics) processJson() OneRecord { - timestamp := float64(0) - state := "" - durtion := float64(0) - for key, value := range p.OriginalValue { - if key == "flag" { - if value.(string) == "1" { - state = "offcpu" - } else { - state = "oncpu" - } - } else if key == "time" { - durtion, _ = strconv.ParseFloat(value.(string), 64) - p.NowTime = p.NowTime + durtion - timestamp = p.NowTime - } else { - continue - } - } - - onerecord := OneRecord{TimeStamp: timestamp, State: state, Durtion: durtion} - return onerecord -} - -// UpdateRecords 对数据进行更新 -func (p *ProcMetrics) UpdateRecords() { - log.Println(p.OriginalValue) - if len(p.Records) < p.Max_records { - p.Records = append(p.Records, p.processJson()) - } else { - p.Records = append(p.Records, p.processJson()) - p.Records = p.Records[1:] - } -} - -// GetRecordsJSON 将json数据解析为byte数据用于渲染到http中 -func (p *ProcMetrics) GetRecordsJSON() ([]byte, error) { - p.mu.Lock() - defer p.mu.Unlock() - return json.Marshal(p.Records) -} - -// BootProcService 启动http服务,为grafana暴露http接口,以供数据调用。 -func (p *ProcMetrics) BootProcService() { - go http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { - recordsJson, err := p.GetRecordsJSON() - if err != nil { - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(recordsJson) - }) - go func() { - if err := http.ListenAndServe(":8090", nil); err != nil { - log.Fatalf("Failed to start HTTP server:", err) - } - }() - select {} -} diff --git a/MagicEyes/src/visualization/prom_core/supplyforTmux.go b/MagicEyes/src/visualization/prom_core/supplyforTmux.go deleted file mode 100644 index 3beb651b3..000000000 --- a/MagicEyes/src/visualization/prom_core/supplyforTmux.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// author: Gui-Yue -// -// lock_image数据可视化处理的核心逻辑 - -package prom_core - -import ( - "visualization/dao" - "encoding/json" - "log" - "net/http" - "strconv" - "sync" - "time" -) - -// 定义单条记录的数据结构 -type TmuxMetrics struct { - Max_records int - NowTime float64 - mu sync.Mutex - OriginalValue map[string]interface{} - flag int - Records []Tmux_OneRecord - Sqlinted bool - Sqlobj *dao.Sqlobj -} - -// 原始数据处理后所保留的基本数据 -type Tmux_OneRecord struct { - TimeStamp float64 `json:"time_stamp"` - RequestState string `json:"request_state"` - HoldState string `json:"hold_state"` - RequestDuration float64 `json:"request_duration"` - HoldDuration float64 `json:"hold_duration"` -} - -// Getorigindata 实现通信,获取原始数据 -func (t *TmuxMetrics) Getorigindata(originalvalue chan map[string]interface{}) { - t.OriginalValue = <-originalvalue -} - -// 数据库操作 -func (t *TmuxMetrics) UpdateSql() { - t.Sqlobj.Data = t.OriginalValue - t.Sqlobj.CreateRow() -} - -func (t *TmuxMetrics) Initsql() { - t.Sqlobj.Data = t.OriginalValue - t.Sqlobj.Connectsql() - t.Sqlobj.OperateTable("tmux_data", "") - t.Sqlinted = true -} - -// // processJson 实现将原始数据进行处理,获取展示所需要的基本数据 -func (t *TmuxMetrics) processJson() Tmux_OneRecord { - t.flag = 0 - timestamp := float64(0) - reqstate := "" - holdstate := "" - reqduration := float64(0) - holdduration := float64(0) - for key, value := range t.OriginalValue { - if key == "unlock_time(ns)" { - if value.(string) != "0" { - t.flag += 1 - } - } else if key == "acq_time(us)" { - if value.(string) != "0.000" { - t.flag += 1 - } - reqduration, _ = strconv.ParseFloat(value.(string), 64) - t.NowTime = t.NowTime + reqduration/float64(time.Microsecond) - timestamp = t.NowTime - } else if key == "hold_time(us)" { - if value.(string) != "0.000" { - t.flag += 1 - } - holdduration, _ = strconv.ParseFloat(value.(string), 64) - t.NowTime = t.NowTime + holdduration/float64(time.Microsecond) - timestamp = t.NowTime - } else { - continue - } - } - if t.flag == 0 { - reqstate = "on_request" - holdstate = "off_hold" - } else if t.flag == 1 { - holdstate = "on_hold" - reqstate = "off_request" - } else if t.flag == 3 { - reqstate = "off_request" - holdstate = "off_hold" - } - record := Tmux_OneRecord{ - TimeStamp: timestamp, - RequestState: reqstate, - HoldState: holdstate, - RequestDuration: reqduration, - HoldDuration: holdduration, - } - return record -} - -// UpdateRecords 对数据进行更新 -func (t *TmuxMetrics) UpdateRecords() { - log.Println(t.OriginalValue) - if len(t.Records) < t.Max_records { - t.Records = append(t.Records, t.processJson()) - } else { - t.Records = append(t.Records, t.processJson()) - t.Records = t.Records[1:] - } -} - -// GetRecordsJSON 将json数据解析为byte数据用于渲染到http中 -func (t *TmuxMetrics) GetRecordsJSON() ([]byte, error) { - t.mu.Lock() - defer t.mu.Unlock() - return json.Marshal(t.Records) -} - -// BootProcService 启动http服务,为grafana暴露http接口,以供数据调用。 -func (t *TmuxMetrics) BootProcService() { - go http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { - recordsJson, err := t.GetRecordsJSON() - if err != nil { - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(recordsJson) - }) - go func() { - if err := http.ListenAndServe(":8090", nil); err != nil { - log.Fatalf("Failed to start HTTP server:", err) - } - }() - select {} -} diff --git a/MagicEyes/src/visualization/run_images.sh b/MagicEyes/src/visualization/run_images.sh deleted file mode 100644 index 6a4b6ce99..000000000 --- a/MagicEyes/src/visualization/run_images.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# ------------------------------------------------------------ -# 拷贝自:https://github.com/linuxkerneltravel/lmp/blob/develop/eBPF_Visualization/eBPF_prometheus/runimages.sh -# ------------------------------------------------------------ -# 定义镜像名 -prometheus_image="prom/prometheus" -grafana_image="grafana/grafana-enterprise" - -# 使用 docker ps 命令列出所有容器的 ID,过滤出指定镜像的容器 -prometheus_info=$(sudo docker ps -a -q --filter "ancestor=$prometheus_image") -grafana_info=$(sudo docker ps -a -q --filter "ancestor=$grafana_image") - -# 检查 Prometheus 容器是否存在 -if [ -n "$prometheus_info" ]; then - # 如果容器存在,获取容器的 ID - # 即获取存储在 $prometheus_info 变量中的容器 ID 列表的第一个容器 ID,并将其存储到 container_id 变量中 - container_id=$(echo "$prometheus_info" | head -n 1) - echo "prometheus 容器存在,id为$container_id。启动容器..." - sudo docker start $container_id -else - echo "容器不存在,开始创建容器,并启动服务" - # 启动一个新的 Prometheus 容器,映射主机的端口 9090 到容器的端口 9090, - # 同时将主机上的 Prometheus 配置文件挂载到容器内,以便配置 Prometheus 服务 - sudo docker run \ - -p 9090:9090 \ - -v ./prom_core/prometheus.yaml:/etc/prometheus/prometheus.yml \ - --name=prometheus prom/prometheus & -fi - -if [ -n "$grafana_info" ]; then - # 如果容器存在,获取容器的 ID - # 即获取存储在 $grafana_info 变量中的容器 ID 列表的第一个容器 ID,并将其存储到 container_id 变量中 - container_id=$(echo "$grafana_info" | head -n 1) - echo "grafana 容器存在,id为$container_id。启动容器..." - sudo docker start $container_id -else - echo "grafana容器不存在,开始创建容器,并启动服务" - # 启动一个新的 Grafana Enterprise 容器,映射主机的端口 3000 到容器的端口 3000,并指定容器名称为 "grafana" - sudo docker run -d -p 3000:3000 --name=grafana grafana/grafana-enterprise & -fi diff --git a/MagicEyes/src/visualization/vis.sh b/eBPF_Visualization/eBPF_prometheus/vis.sh similarity index 75% rename from MagicEyes/src/visualization/vis.sh rename to eBPF_Visualization/eBPF_prometheus/vis.sh index 5485b87d8..ea6861b19 100755 --- a/MagicEyes/src/visualization/vis.sh +++ b/eBPF_Visualization/eBPF_prometheus/vis.sh @@ -14,10 +14,10 @@ PARAM4=$4 OTHER_ARGS=${@:5} # 从第5个参数开始的所有额外参数 # 构建路径 -TARGET_DIR="../../build/src/backend/${PARAM1}/${PARAM2}/${PARAM3}" +TARGET_DIR="../backend/${PARAM1}/${PARAM2}/bin" # 构建命令 -COMMAND="./data-visual collect ${TARGET_DIR} ${PARAM4} ${OTHER_ARGS}" +COMMAND="sudo ./data-visual collect ${TARGET_DIR}/${PARAM3} ${PARAM4} ${OTHER_ARGS}" # 显示即将执行的命令(可选) echo "Executing command: ${COMMAND}"