diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 7f4aaa33..63a3bed5 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -640,6 +640,7 @@ func init() { rootCmd.PersistentFlags().IntVar(&gconfig.BrkPid, "brk-pid", -1, "set hardware breakpoint pid, just keep default") rootCmd.PersistentFlags().StringVar(&gconfig.BrkLib, "brk-lib", "", "as library base address, work with -p/--pid option") rootCmd.PersistentFlags().Uint64Var(&gconfig.BrkLen, "brk-len", 4, "hardware breakpoint length, default 4, support [1, 8]") + rootCmd.PersistentFlags().StringArrayVar(&gconfig.BrkPoint, "brk-point", []string{}, "read args when hardware breakpoint hits, e.g. buf:128:x0 or [int:x1,buf:64:x0+8]") // 缓冲区大小设定 单位M rootCmd.PersistentFlags().Uint32VarP(&gconfig.Buffer, "buffer", "b", 8, "perf cache buffer size, default 8M") rootCmd.PersistentFlags().Uint32Var(&gconfig.MaxOp, "maxop", 64, "max operation count for uprobe, at least 192 for string array") @@ -665,6 +666,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&gconfig.RegName, "reg", "", "get the offset of reg") rootCmd.PersistentFlags().BoolVarP(&gconfig.DumpRet, "dumpret", "", false, "dump ret offset for symbol") rootCmd.PersistentFlags().BoolVarP(&gconfig.DumpHex, "dumphex", "", false, "dump buffer as hex") + rootCmd.PersistentFlags().BoolVarP(&gconfig.DumpBase64, "dumpbase64", "", false, "dump buffer as base64") rootCmd.PersistentFlags().BoolVarP(&gconfig.ShowPC, "showpc", "", false, "show origin pc register value") rootCmd.PersistentFlags().BoolVarP(&gconfig.ShowTime, "showtime", "", false, "show event boot time info") rootCmd.PersistentFlags().BoolVarP(&gconfig.ShowUid, "showuid", "", false, "show process uid info") diff --git a/src/common/buffer.h b/src/common/buffer.h index f1dd35e4..44e10d35 100644 --- a/src/common/buffer.h +++ b/src/common/buffer.h @@ -197,7 +197,7 @@ static __always_inline int save_str_to_buf(event_data_t *event, void *ptr, u8 in static __always_inline int save_utf16_to_buf(event_data_t *event, void *ptr, u8 index) { // UTF16 最大字节数(必须是偶数) - int max_bytes = 512; + int max_bytes = MAX_BUF_READ_SIZE; // 直接调用 save_bytes_to_buf return save_bytes_to_buf(event, ptr, max_bytes, index); } diff --git a/tests/config_uprobe_test_base64.json b/tests/config_uprobe_test_base64.json new file mode 100644 index 00000000..18d091af --- /dev/null +++ b/tests/config_uprobe_test_base64.json @@ -0,0 +1,18 @@ +{ + "type": "uprobe", + "library": "libtest.so", + "points": [ + { + "name": "testBytesBase64", + "params": [ + { + "name": "buf_bytes", + "type": "buf", + "reg": "x0", + "size": "x1", + "format": "base64" + } + ] + } + ] +} \ No newline at end of file diff --git a/user/argtype/argtype_base.go b/user/argtype/argtype_base.go index 4d3688c8..192eef72 100644 --- a/user/argtype/argtype_base.go +++ b/user/argtype/argtype_base.go @@ -587,6 +587,9 @@ func (this *ARG_BUFFER) ParseArg(ptr uint64, buf *bytes.Buffer, parse_more, fmt_ (this.ParseImpl).(IArgBuffer).SetArgPayload(payload) } if !fmt_json { + if this.DumpBase64 { + return fmt.Sprintf("0x%x%s", ptr, this.ParseImpl.HexToBase64Format()) + } if this.DumpHex { return fmt.Sprintf("0x%x%s", ptr, this.ParseImpl.HexFormat(this.Color)) } diff --git a/user/argtype/config_struct.go b/user/argtype/config_struct.go index 24f24ee2..1369f294 100644 --- a/user/argtype/config_struct.go +++ b/user/argtype/config_struct.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "encoding/base64" "fmt" "net" "stackplz/user/util" @@ -50,6 +51,7 @@ type IParseStruct interface { GetArgStruct() *Arg_struct Format() string HexFormat(bool) string + HexToBase64Format() string } // 结构体类型 @@ -115,6 +117,10 @@ func (this *Arg_buffer) HexFormat(color bool) string { return "()" } +func (this *Arg_buffer) HexToBase64Format() string { + return fmt.Sprintf(" (base64:%s)", base64.StdEncoding.EncodeToString(this.ArgPayload)) +} + func (this *Arg_buffer) MarshalJSON() ([]byte, error) { type ArgStructAlias Arg_struct return json.Marshal(&struct { @@ -143,6 +149,10 @@ func (this *Arg_string) HexFormat(color bool) string { return this.Format() } +func (this *Arg_string) HexToBase64Format() string { + return this.Format() +} + func (this *Arg_string) Format() string { return fmt.Sprintf("(%s)", util.B2STrim(this.ArgPayload)) } @@ -190,6 +200,10 @@ func (this *Arg_string16) HexFormat(color bool) string { return this.Format() } +func (this *Arg_string16) HexToBase64Format() string { + return this.Format() +} + func (this *Arg_string16) Format() string { // UTF‑16LE → UTF‑8 s := utf16leToUtf8(this.ArgPayload) @@ -237,6 +251,10 @@ func (this *Arg_Sigaction) HexFormat(color bool) string { return this.Format() } +func (this *Arg_Sigaction) HexToBase64Format() string { + return this.Format() +} + func (this *Arg_Sigaction) Format() string { var fields []string fields = append(fields, fmt.Sprintf("sa_handler=0x%x", this.Sa_handler)) @@ -288,6 +306,10 @@ func (this *Arg_Timespec) HexFormat(color bool) string { return this.Format() } +func (this *Arg_Timespec) HexToBase64Format() string { + return this.Format() +} + func (this *Arg_Timespec) Format() string { var fields []string fields = append(fields, fmt.Sprintf("sec=%d", this.Sec)) diff --git a/user/argtype/iargtype.go b/user/argtype/iargtype.go index 90d5de2c..c999c9d0 100644 --- a/user/argtype/iargtype.go +++ b/user/argtype/iargtype.go @@ -25,8 +25,10 @@ type IArgType interface { GetTypeIndex() uint32 SetParentIndex(uint32) SetDumpHex(bool) + SetDumpBase64(bool) SetColor(bool) GetDumpHex() bool + GetDumpBase64() bool GetColor() bool GetParentIndex() uint32 GetSize() uint32 @@ -54,6 +56,7 @@ type ArgType struct { ParseCB ParseFN ParseImpl IParseStruct DumpHex bool + DumpBase64 bool Color bool } @@ -77,6 +80,7 @@ func (this *ArgType) Clone() IArgType { at.ParseCB = this.ParseCB at.ParseImpl = this.ParseImpl at.DumpHex = this.DumpHex + at.DumpBase64 = this.DumpBase64 at.Color = this.Color return &at } @@ -105,6 +109,10 @@ func (this *ArgType) SetDumpHex(dump_hex bool) { this.DumpHex = dump_hex } +func (this *ArgType) SetDumpBase64(dump_base64 bool) { + this.DumpBase64 = dump_base64 +} + func (this *ArgType) SetColor(color bool) { this.Color = color } @@ -113,6 +121,10 @@ func (this *ArgType) GetDumpHex() bool { return this.DumpHex } +func (this *ArgType) GetDumpBase64() bool { + return this.DumpBase64 +} + func (this *ArgType) GetColor() bool { return this.Color } diff --git a/user/config/config_brk.go b/user/config/config_brk.go new file mode 100644 index 00000000..23fb1787 --- /dev/null +++ b/user/config/config_brk.go @@ -0,0 +1,90 @@ +package config + +import ( + "errors" + "fmt" + "regexp" + "strings" + + . "stackplz/user/common" +) + +type BrkPointConfig struct { + ArgsStr string + PointArgs []*PointArg + DumpHex bool + DumpBase64 bool + Color bool +} + +func (this *BrkPointConfig) IsEnable() bool { + return len(this.PointArgs) > 0 +} + +func (this *BrkPointConfig) SetDumpHex(dumpHex bool) { + this.DumpHex = dumpHex +} + +func (this *BrkPointConfig) SetDumpBase64(dumpBase64 bool) { + this.DumpBase64 = dumpBase64 +} + +func (this *BrkPointConfig) SetColor(color bool) { + this.Color = color +} + +func (this *BrkPointConfig) Parse_BrkPoint(configs []string) error { + if len(configs) == 0 { + return nil + } + parser := &StackUprobeConfig{} + parser.SetDumpHex(this.DumpHex) + parser.SetDumpBase64(this.DumpBase64) + parser.SetColor(this.Color) + + var allArgs []string + for _, configStr := range configs { + configStr = strings.TrimSpace(configStr) + if configStr == "" { + continue + } + argsStr, err := extractBrkArgs(configStr) + if err != nil { + return err + } + for _, argStr := range strings.Split(argsStr, ",") { + argStr = strings.TrimSpace(argStr) + if argStr != "" { + allArgs = append(allArgs, argStr) + } + } + } + + this.ArgsStr = strings.Join(allArgs, ",") + for argIndex, argStr := range allArgs { + argName := fmt.Sprintf("arg_%d", argIndex) + pointArg := NewUprobePointArg(argName, POINTER, uint32(argIndex)) + if err := parser.ParseArgType(argStr, pointArg); err != nil { + return err + } + this.PointArgs = append(this.PointArgs, pointArg) + } + return nil +} + +func extractBrkArgs(configStr string) (string, error) { + if strings.HasPrefix(configStr, "[") && strings.HasSuffix(configStr, "]") { + return strings.TrimSpace(configStr[1 : len(configStr)-1]), nil + } + + reg := regexp.MustCompile(`\[(.+)\]`) + match := reg.FindStringSubmatch(configStr) + if len(match) == 2 { + return strings.TrimSpace(match[1]), nil + } + + if strings.Contains(configStr, "[") || strings.Contains(configStr, "]") { + return "", errors.New(fmt.Sprintf("parse brk point args failed: %s", configStr)) + } + return configStr, nil +} diff --git a/user/config/config_file.go b/user/config/config_file.go index c1771d08..490aff16 100644 --- a/user/config/config_file.go +++ b/user/config/config_file.go @@ -129,6 +129,8 @@ func (this *ParamConfig) GetPointArg(arg_index, point_type uint32) *PointArg { } switch this.Format { + case "base64": + point_arg.SetDumpBase64(true) case "hex": point_arg.SetHexFormat() case "hexdump": diff --git a/user/config/config_global.go b/user/config/config_global.go index 0a439dbe..7461541b 100644 --- a/user/config/config_global.go +++ b/user/config/config_global.go @@ -48,6 +48,7 @@ type GlobalConfig struct { BrkAddr string BrkLib string BrkLen uint64 + BrkPoint []string LogFile string DumpFile string ParseFile string @@ -58,6 +59,7 @@ type GlobalConfig struct { RegName string DumpRet bool DumpHex bool + DumpBase64 bool ShowPC bool ShowTime bool ShowUid bool diff --git a/user/config/config_module.go b/user/config/config_module.go index b98d2d1b..85018423 100644 --- a/user/config/config_module.go +++ b/user/config/config_module.go @@ -27,6 +27,7 @@ type StackUprobeConfig struct { NonElfOffset uint64 Points []*UprobeArgs DumpHex bool + DumpBase64 bool Color bool } @@ -177,6 +178,7 @@ func (this *StackUprobeConfig) ParseArgType(arg_str string, point_arg *PointArg) } } at.SetDumpHex(this.DumpHex) + at.SetDumpBase64(this.DumpBase64) at.SetColor(this.Color) point_arg.SetTypeIndex(at.GetTypeIndex()) // 这个设定用于指示是否进一步读取和解析 @@ -266,6 +268,10 @@ func (this *StackUprobeConfig) SetDumpHex(dump_hex bool) { this.DumpHex = dump_hex } +func (this *StackUprobeConfig) SetDumpBase64(dump_base64 bool) { + this.DumpBase64 = dump_base64 +} + func (this *StackUprobeConfig) SetColor(color bool) { this.Color = color } @@ -443,6 +449,7 @@ type SyscallConfig struct { SysWhitelist []uint32 SysBlacklist []uint32 DumpHex bool + DumpBase64 bool Color bool } @@ -458,6 +465,10 @@ func (this *SyscallConfig) SetDumpHex(dump_hex bool) { this.DumpHex = dump_hex } +func (this *SyscallConfig) SetDumpBase64(dump_base64 bool) { + this.DumpBase64 = dump_base64 +} + func (this *SyscallConfig) SetColor(color bool) { this.Color = color } @@ -539,6 +550,7 @@ func (this *SyscallConfig) Parse_FileConfig(config *SyscallFileConfig) (err erro point_arg := param.GetPointArg(uint32(arg_index), point_type) point_arg.SetDumpHex(this.DumpHex) + point_arg.SetDumpBase64(this.DumpBase64) point_arg.SetColor(this.Color) a_p := point_arg.Clone() @@ -785,10 +797,12 @@ type ModuleConfig struct { BrkLen uint64 BrkType uint32 BrkKernel bool + BrkPointConf *BrkPointConfig Color bool DumpHandle *os.File FmtJson bool DumpHex bool + DumpBase64 bool ShowPC bool ShowTime bool ShowUid bool @@ -854,6 +868,7 @@ func (this *ModuleConfig) InitCommonConfig(gconfig *GlobalConfig) { this.FmtJson = gconfig.FmtJson this.RegName = gconfig.RegName this.DumpHex = gconfig.DumpHex + this.DumpBase64 = gconfig.DumpBase64 this.ShowPC = gconfig.ShowPC this.ShowTime = gconfig.ShowTime this.ShowUid = gconfig.ShowUid @@ -864,12 +879,22 @@ func (this *ModuleConfig) InitCommonConfig(gconfig *GlobalConfig) { this.StackUprobeConf = &StackUprobeConfig{} this.StackUprobeConf.SetDumpHex(this.DumpHex) + this.StackUprobeConf.SetDumpBase64((this.DumpBase64)) this.StackUprobeConf.SetColor(this.Color) + this.BrkPointConf = &BrkPointConfig{} + this.BrkPointConf.SetDumpHex(this.DumpHex) + this.BrkPointConf.SetDumpBase64(this.DumpBase64) + this.BrkPointConf.SetColor(this.Color) + if err := this.BrkPointConf.Parse_BrkPoint(gconfig.BrkPoint); err != nil { + panic(err) + } + this.SysCallConf = &SyscallConfig{} this.SysCallConf.SetDebug(this.Debug) this.SysCallConf.SetLogger(this.logger) this.SysCallConf.SetDumpHex(this.DumpHex) + this.SysCallConf.SetDumpBase64((this.DumpBase64)) this.SysCallConf.SetColor(this.Color) } diff --git a/user/config/config_point_arg.go b/user/config/config_point_arg.go index 9b453219..8e1aba0d 100644 --- a/user/config/config_point_arg.go +++ b/user/config/config_point_arg.go @@ -32,6 +32,10 @@ func (this *PointArg) SetDumpHex(dump_hex bool) { argtype.GetArgType(this.TypeIndex).SetDumpHex(dump_hex) } +func (this *PointArg) SetDumpBase64(dump_base64 bool) { + argtype.GetArgType(this.TypeIndex).SetDumpBase64(dump_base64) +} + func (this *PointArg) SetColor(color bool) { argtype.GetArgType(this.TypeIndex).SetColor(color) } diff --git a/user/event/event_brk.go b/user/event/event_brk.go index dd9bfa23..9031866c 100644 --- a/user/event/event_brk.go +++ b/user/event/event_brk.go @@ -14,10 +14,14 @@ type BrkEvent struct { ContextEvent EventAddr uint64 UUID string + ArgStr string } func (this *BrkEvent) String() (s string) { s = fmt.Sprintf("[%s] event_addr:0x%x hit_count:%d", this.GetUUID(), this.EventAddr, hit_count) + if this.ArgStr != "" { + s += " args" + this.ArgStr + } s = this.GetStackTrace(s) return s } @@ -80,10 +84,19 @@ func (this *BrkEvent) ParseContext() (err error) { return err } this.ParseContextStack() + this.ParseBrkArgs() return nil } +func (this *BrkEvent) ParseBrkArgs() { + if this.mconf.BrkPointConf == nil || !this.mconf.BrkPointConf.IsEnable() { + return + } + reader := newBrkArgReader(this.GetPid(), this) + this.ArgStr = reader.formatArgs(this.mconf.BrkPointConf.PointArgs) +} + func (this *BrkEvent) Clone() IEventStruct { event := new(BrkEvent) return event diff --git a/user/event/event_brk_args.go b/user/event/event_brk_args.go new file mode 100644 index 00000000..5e786ec8 --- /dev/null +++ b/user/event/event_brk_args.go @@ -0,0 +1,390 @@ +package event + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "os" + "strings" + + "stackplz/user/argtype" + "stackplz/user/common" + "stackplz/user/config" +) + +const maxStringReadSize = 16384 + +type brkArgReader struct { + pid uint32 + event *BrkEvent + mem *os.File +} + +type brkOpCtx struct { + saveIndex uint8 + regIndex uint8 + loopCount uint8 + breakCount uint8 + loopIndex int + opKeyIndex int + postCode uint32 + readLen uint32 + readAddr uint64 + regValue uint64 + pointerValue uint64 + tmpValue uint64 + saved bytes.Buffer +} + +func newBrkArgReader(pid uint32, event *BrkEvent) *brkArgReader { + return &brkArgReader{pid: pid, event: event} +} + +func (this *brkArgReader) Close() { + if this.mem != nil { + this.mem.Close() + this.mem = nil + } +} + +func (this *brkArgReader) formatArgs(pointArgs []*config.PointArg) string { + if len(pointArgs) == 0 { + return "" + } + defer this.Close() + + results := make([]string, 0, len(pointArgs)) + for _, pointArg := range pointArgs { + result, err := this.formatArg(pointArg) + if err != nil { + result = fmt.Sprintf("", err) + } + results = append(results, fmt.Sprintf("%s=%s", pointArg.Name, result)) + } + return "(" + strings.Join(results, ", ") + ")" +} + +func (this *brkArgReader) formatArg(pointArg *config.PointArg) (result string, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + } + }() + + raw, err := this.runPointArg(pointArg) + if err != nil { + return "", err + } + buf := bytes.NewBuffer(raw) + var ptr argtype.Arg_reg + if err := binary.Read(buf, binary.LittleEndian, &ptr); err != nil { + return "", err + } + return pointArg.Parse(ptr.Address, buf, config.EBPF_UPROBE_ENTER), nil +} + +func (this *brkArgReader) runPointArg(pointArg *config.PointArg) ([]byte, error) { + ctx := &brkOpCtx{saveIndex: 0, opKeyIndex: 0, postCode: argtype.OP_SKIP} + opKeys := pointArg.GetOpList() + var currentOp *argtype.OpConfig + + for i := 0; i < common.MAX_OP_COUNT; i++ { + var code uint32 + if currentOp != nil && ctx.postCode != argtype.OP_SKIP { + code = ctx.postCode + ctx.postCode = argtype.OP_SKIP + } else { + if ctx.opKeyIndex >= len(opKeys) { + break + } + currentOp = argtype.OPM.GetOp(opKeys[ctx.opKeyIndex]) + ctx.opKeyIndex++ + code = currentOp.Code + ctx.postCode = currentOp.PostCode + } + + if code == argtype.OP_SKIP { + break + } + this.runOp(ctx, currentOp, code) + } + + if ctx.saved.Len() == 0 { + return nil, fmt.Errorf("no brk arg data saved") + } + return ctx.saved.Bytes(), nil +} + +func (this *brkArgReader) runOp(ctx *brkOpCtx, op *argtype.OpConfig, code uint32) { + switch code { + case argtype.OP_RESET_CTX: + ctx.breakCount = 0 + ctx.regIndex = 0 + ctx.readAddr = 0 + ctx.readLen = 0 + ctx.regValue = 0 + ctx.pointerValue = 0 + case argtype.OP_SET_REG_INDEX: + ctx.regIndex = uint8(op.Value) + case argtype.OP_SET_READ_LEN: + ctx.readLen = uint32(op.Value) + case argtype.OP_SET_READ_LEN_REG_VALUE: + if uint64(ctx.readLen) > ctx.regValue { + ctx.readLen = uint32(ctx.regValue) + } + case argtype.OP_SET_READ_LEN_POINTER_VALUE: + if uint64(ctx.readLen) > ctx.pointerValue { + ctx.readLen = uint32(ctx.pointerValue) + } + case argtype.OP_SET_READ_COUNT: + ctx.readLen *= uint32(op.Value) + case argtype.OP_ADD_OFFSET: + ctx.readAddr += op.Value + case argtype.OP_SUB_OFFSET: + ctx.readAddr -= op.Value + case argtype.OP_MOVE_REG_VALUE: + ctx.readAddr = ctx.regValue + case argtype.OP_MOVE_POINTER_VALUE: + ctx.readAddr = ctx.pointerValue + case argtype.OP_MOVE_TMP_VALUE: + ctx.readAddr = ctx.tmpValue + case argtype.OP_SET_TMP_VALUE: + ctx.tmpValue = ctx.readAddr + case argtype.OP_FOR_BREAK: + if ctx.loopCount == 0 { + ctx.loopIndex = ctx.opKeyIndex + } + if ctx.loopCount >= ctx.breakCount { + ctx.loopCount = 0 + ctx.breakCount = 0 + ctx.loopIndex = 0 + } else { + ctx.loopCount++ + ctx.opKeyIndex = ctx.loopIndex + } + case argtype.OP_SET_BREAK_COUNT: + ctx.breakCount = common.MAX_LOOP_COUNT + if uint64(ctx.breakCount) > op.Value { + ctx.breakCount = uint8(op.Value) + } + case argtype.OP_SET_BREAK_COUNT_REG_VALUE: + ctx.breakCount = common.MAX_LOOP_COUNT + if uint64(ctx.breakCount) > ctx.regValue { + ctx.breakCount = uint8(ctx.regValue) + } + case argtype.OP_SET_BREAK_COUNT_POINTER_VALUE: + ctx.breakCount = common.MAX_LOOP_COUNT + if uint64(ctx.breakCount) > ctx.pointerValue { + ctx.breakCount = uint8(ctx.pointerValue) + } + case argtype.OP_SAVE_ADDR: + ctx.saveValue(ctx.readAddr) + case argtype.OP_ADD_REG: + ctx.readAddr += ctx.regValue + case argtype.OP_SUB_REG: + ctx.readAddr -= ctx.regValue + case argtype.OP_READ_REG: + if op.PreCode == argtype.OP_SET_REG_INDEX { + ctx.regIndex = uint8(op.Value) + } + ctx.regValue = this.regValue(uint32(ctx.regIndex)) + case argtype.OP_SAVE_REG: + ctx.saveValue(ctx.regValue) + case argtype.OP_READ_POINTER: + addr := ctx.readAddr + if op.PreCode == argtype.OP_ADD_OFFSET { + addr += op.Value + } else if op.PreCode == argtype.OP_SUB_OFFSET { + addr -= op.Value + } + ctx.pointerValue = this.readPointer(addr) + case argtype.OP_SAVE_POINTER: + ctx.saveValue(ctx.pointerValue) + case argtype.OP_SAVE_STRUCT: + ctx.readAddr = fixUserAddr(ctx.readAddr) + if op.PreCode == argtype.OP_SET_READ_COUNT { + ctx.readLen *= uint32(op.Value) + } + if ctx.readLen > common.MAX_BUF_READ_SIZE { + ctx.readLen = common.MAX_BUF_READ_SIZE + } + payload, err := this.readMemory(ctx.readAddr, ctx.readLen) + if err != nil { + payload = nil + } + ctx.saveBytes(payload, uint32(len(payload))) + case argtype.OP_SAVE_STRING: + ctx.readAddr = fixUserAddr(ctx.readAddr) + payload, err := this.readString(ctx.readAddr) + if err != nil { + payload = nil + } + ctx.saveBytes(payload, uint32(len(payload))) + case argtype.OP_SAVE_PTR_STRING: + ptr := this.readPointer(ctx.readAddr) + ctx.saveValue(ptr) + payload, err := this.readString(fixUserAddr(ptr)) + if ptr == 0 || err != nil { + ctx.saveBytes(nil, common.STRARR_MAGIC_LEN) + ctx.loopCount = ctx.breakCount + } else { + ctx.saveBytes(payload, uint32(len(payload))) + } + case argtype.OP_SAVE_STRING16: + ctx.readAddr = fixUserAddr(ctx.readAddr) + payload, err := this.readMemory(ctx.readAddr, common.MAX_BUF_READ_SIZE) + if err != nil { + payload = nil + } + ctx.saveBytes(payload, uint32(len(payload))) + case argtype.OP_SAVE_PTR_STRING16: + ptr := this.readPointer(ctx.readAddr) + ctx.saveValue(ptr) + payload, err := this.readMemory(fixUserAddr(ptr), common.MAX_BUF_READ_SIZE) + if ptr == 0 || err != nil { + ctx.saveBytes(nil, common.STRARR_MAGIC_LEN) + ctx.loopCount = ctx.breakCount + } else { + ctx.saveBytes(payload, uint32(len(payload))) + } + case argtype.OP_READ_STD_STRING: + ptr := fixUserAddr(ctx.readAddr) + value := this.readByte(ptr) + if value&1 == 0 { + ptr += 1 + } else { + ptr += 16 + ptr = this.readPointer(ptr) + } + ctx.readAddr = ptr + case argtype.OP_READ_IL2CPP_STRING: + ctx.readAddr += 0x14 + case argtype.OP_FILTER_VALUE, argtype.OP_FILTER_BUFFER, argtype.OP_FILTER_STRING: + // Brk arg capture runs after perf sample delivery, so filters are not + // applied here. Keep the op as a no-op to preserve read formatting. + default: + } +} + +func (ctx *brkOpCtx) saveValue(value uint64) { + ctx.saved.WriteByte(ctx.saveIndex) + _ = binary.Write(&ctx.saved, binary.LittleEndian, value) + ctx.saveIndex++ +} + +func (ctx *brkOpCtx) saveBytes(payload []byte, savedLen uint32) { + ctx.saved.WriteByte(ctx.saveIndex) + _ = binary.Write(&ctx.saved, binary.LittleEndian, savedLen) + if len(payload) > 0 { + ctx.saved.Write(payload) + } + ctx.saveIndex++ +} + +func (this *brkArgReader) regValue(regIndex uint32) uint64 { + var regs []uint64 + if this.event.rec.ExtraOptions.UnwindStack { + regs = this.event.UnwindBuffer.Regs + } else { + regs = this.event.RegsBuffer.Regs + } + if int(regIndex) >= len(regs) { + return 0 + } + return regs[regIndex] +} + +func (this *brkArgReader) readPointer(addr uint64) uint64 { + ptrSize := uint32(8) + if this.event.mconf.Is32Bit { + ptrSize = 4 + } + payload, err := this.readMemory(fixUserAddr(addr), ptrSize) + if err != nil || uint32(len(payload)) != ptrSize { + return 0 + } + if ptrSize == 4 { + return uint64(binary.LittleEndian.Uint32(payload)) + } + return binary.LittleEndian.Uint64(payload) +} + +func (this *brkArgReader) readByte(addr uint64) byte { + payload, err := this.readMemory(addr, 1) + if err != nil || len(payload) == 0 { + return 0 + } + return payload[0] +} + +func (this *brkArgReader) readString(addr uint64) ([]byte, error) { + addr = fixUserAddr(addr) + if addr == 0 { + return nil, fmt.Errorf("zero address") + } + if err := this.openMem(); err != nil { + return nil, err + } + + var payload []byte + const chunkSize = 256 + for len(payload) < maxStringReadSize { + remain := maxStringReadSize - len(payload) + if remain > chunkSize { + remain = chunkSize + } + chunk := make([]byte, remain) + n, err := this.mem.ReadAt(chunk, int64(addr)+int64(len(payload))) + if n > 0 { + payload = append(payload, chunk[:n]...) + if idx := bytes.IndexByte(chunk[:n], 0); idx >= 0 { + return payload[:len(payload)-n+idx+1], nil + } + } + if err != nil { + if len(payload) > 0 { + return payload, nil + } + return nil, err + } + } + return payload, nil +} + +func (this *brkArgReader) readMemory(addr uint64, size uint32) ([]byte, error) { + if size == 0 { + return []byte{}, nil + } + if addr == 0 { + return nil, fmt.Errorf("zero address") + } + if err := this.openMem(); err != nil { + return nil, err + } + payload := make([]byte, size) + n, err := this.mem.ReadAt(payload, int64(addr)) + if err != nil && err != io.EOF { + if n > 0 { + return payload[:n], err + } + return nil, err + } + return payload[:n], err +} + +func (this *brkArgReader) openMem() error { + if this.mem != nil { + return nil + } + mem, err := os.Open(fmt.Sprintf("/proc/%d/mem", this.pid)) + if err != nil { + return err + } + this.mem = mem + return nil +} + +func fixUserAddr(addr uint64) uint64 { + return addr & 0xffffffffffff +} diff --git a/user/event_parser/parser.go b/user/event_parser/parser.go index c3e1930c..1eaa2bac 100644 --- a/user/event_parser/parser.go +++ b/user/event_parser/parser.go @@ -75,7 +75,7 @@ func (this *EventParser) ParseDump(dump_name string) { rec.ExtraOptions = &perf.ExtraPerfOptions{ UnwindStack: this.mconf.UnwindStack, - ShowRegs: this.mconf.ShowRegs, + ShowRegs: this.mconf.ShowRegs || (this.mconf.BrkPointConf != nil && this.mconf.BrkPointConf.IsEnable()), BrkAddr: this.mconf.BrkAddr, BrkLen: this.mconf.BrkLen, BrkType: this.mconf.BrkType, diff --git a/user/module/imodule.go b/user/module/imodule.go index 00a2d6e1..62558463 100644 --- a/user/module/imodule.go +++ b/user/module/imodule.go @@ -197,6 +197,9 @@ func (this *Module) getExtraOptions(em *ebpf.Map) perf.ExtraPerfOptions { } else { ShowRegs = this.mconf.ShowRegs } + if this.mconf.BrkPointConf != nil && this.mconf.BrkPointConf.IsEnable() { + ShowRegs = true + } BrkPid := this.mconf.BrkPid // 对内核地址断点的时候无法指定pid为用户进程的pid if this.mconf.BrkKernel { diff --git a/user/rpc/rpc.go b/user/rpc/rpc.go index fd80d82c..2aa59a03 100644 --- a/user/rpc/rpc.go +++ b/user/rpc/rpc.go @@ -62,11 +62,19 @@ func BrkIt(opts *BrkOptions) { mconfig.StackSize = Gconfig.StackSize mconfig.ShowRegs = Gconfig.ShowRegs mconfig.GetOff = Gconfig.GetOff + mconfig.Is32Bit = Gconfig.Is32Bit() mconfig.BrkPid = opts.BrkPid mconfig.BrkAddr = opts.BrkAddr mconfig.BrkLen = opts.BrkLen mconfig.BrkType = opts.BrkType mconfig.BrkKernel = false + mconfig.BrkPointConf = &config.BrkPointConfig{} + mconfig.BrkPointConf.SetDumpHex(Gconfig.DumpHex) + mconfig.BrkPointConf.SetDumpBase64(Gconfig.DumpBase64) + mconfig.BrkPointConf.SetColor(Gconfig.Color) + if err := mconfig.BrkPointConf.Parse_BrkPoint(Gconfig.BrkPoint); err != nil { + panic(err) + } mod.Init(Ctx, Logger, mconfig) err := mod.Run() if err != nil {