github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/module/pcdata_patcher.go (about) 1 //go:build go1.15 && !go1.22 2 // +build go1.15,!go1.22 3 4 package module 5 6 import ( 7 _ "unsafe" 8 9 "github.com/Rookout/GoSDK/pkg/rookoutErrors" 10 11 "github.com/go-errors/errors" 12 ) 13 14 type PCDataEntry struct { 15 Offset uintptr 16 Value int32 17 } 18 19 20 func decodePCDataEntries(p []byte) (pcDataEntries []PCDataEntry) { 21 if p == nil { 22 return pcDataEntries 23 } 24 var pc uintptr 25 val := int32(-1) 26 p, ok := step(p, &pc, &val, true) 27 for { 28 if !ok { 29 return pcDataEntries 30 } 31 pcDataEntries = append(pcDataEntries, PCDataEntry{Offset: pc, Value: val}) 32 if len(p) <= 0 { 33 return pcDataEntries 34 } 35 p, ok = step(p, &pc, &val, false) 36 } 37 } 38 39 40 41 func findNewAddressByOriginalAddress(originalAddress uintptr, addressMappings []AddressMapping) (uintptr, bool) { 42 for _, mapping := range addressMappings { 43 if originalAddress == mapping.OriginalAddress { 44 return mapping.NewAddress, true 45 } 46 } 47 return 0, false 48 } 49 50 51 52 func updatePCDataEntries(pcDataEntries []PCDataEntry, offsetMappings []AddressMapping) { 53 for i := 0; i < len(pcDataEntries); i++ { 54 newOffset, found := findNewAddressByOriginalAddress(pcDataEntries[i].Offset, offsetMappings) 55 if found { 56 pcDataEntries[i].Offset = newOffset 57 } 58 } 59 } 60 61 62 63 func getEntryForOffset(offset uintptr, pcDataEntries []PCDataEntry) (int, *PCDataEntry) { 64 prevPCOffset := uintptr(0) 65 for index, pcDataEntry := range pcDataEntries { 66 if prevPCOffset <= offset && pcDataEntry.Offset > offset { 67 return index, &pcDataEntry 68 } 69 prevPCOffset = pcDataEntry.Offset 70 } 71 72 return -1, nil 73 } 74 75 76 func UvarintToBytes(x uint64) (buf []byte) { 77 for x >= 0x80 { 78 buf = append(buf, byte(x)|0x80) 79 x >>= 7 80 } 81 buf = append(buf, byte(x)) 82 return buf 83 } 84 85 86 func writeUvarintToBytes(bytes []byte, x uint64) ([]byte, error) { 87 encodedVariant := UvarintToBytes(x) 88 bytes = append(bytes, encodedVariant...) 89 return bytes, nil 90 } 91 92 func encode(v int32) uint32 { 93 return uint32(v<<1) ^ uint32(v>>31) 94 } 95 96 97 func writePCDataEntry(p []byte, value int32, offset int32) ([]byte, error) { 98 p, err := writeUvarintToBytes(p, uint64(encode(value))) 99 if err != nil { 100 return nil, err 101 } 102 p, err = writeUvarintToBytes(p, uint64(offset/pcQuantum)) 103 if err != nil { 104 return nil, err 105 } 106 107 return p, nil 108 } 109 110 func removeDuplicateValues(pcDataEntries []PCDataEntry) (noDups []PCDataEntry) { 111 if len(pcDataEntries) == 0 { 112 return nil 113 } 114 for i := range pcDataEntries { 115 if i == len(pcDataEntries)-1 { 116 noDups = append(noDups, pcDataEntries[i]) 117 break 118 } 119 120 if pcDataEntries[i].Value == pcDataEntries[i+1].Value { 121 continue 122 } 123 noDups = append(noDups, pcDataEntries[i]) 124 } 125 126 return noDups 127 } 128 129 130 func encodePCDataEntries(pcDataEntries []PCDataEntry) (encoded []byte, err error) { 131 if len(pcDataEntries) == 0 { 132 return nil, nil 133 } 134 encoded = make([]byte, 0, len(pcDataEntries)*20) 135 prevOffset := int32(0) 136 prevValue := int32(-1) 137 138 for _, newPair := range pcDataEntries { 139 valueDelta := newPair.Value - prevValue 140 offsetDelta := int32(newPair.Offset) - prevOffset 141 encoded, err = writePCDataEntry(encoded, valueDelta, offsetDelta) 142 if err != nil { 143 return nil, err 144 } 145 prevOffset = int32(newPair.Offset) 146 prevValue = newPair.Value 147 } 148 149 encoded = append(encoded, 0) 150 return encoded, nil 151 } 152 153 154 155 156 157 158 159 160 161 func addCallbackEntry(pcDataEntries []PCDataEntry, callbackOffsetStart, callbackOffsetEnd uintptr, pcDataGenerator func(uintptr, uintptr, int32) ([]PCDataEntry, error)) ([]PCDataEntry, error) { 162 callbackEntryIndex, entryForCallback := getEntryForOffset(callbackOffsetStart, pcDataEntries) 163 if callbackEntryIndex == -1 { 164 return nil, errors.New("No PCData entry in table after breakpoint") 165 } 166 167 newPCDataEntries := pcDataEntries 168 callbackPCDataEntries, err := pcDataGenerator(callbackOffsetStart, callbackOffsetEnd, entryForCallback.Value) 169 if err != nil { 170 return nil, err 171 } 172 fromCallbackUntilEnd := append(callbackPCDataEntries, newPCDataEntries[callbackEntryIndex:]...) 173 newPCDataEntries = append(newPCDataEntries[:callbackEntryIndex], fromCallbackUntilEnd...) 174 return newPCDataEntries, nil 175 } 176 177 func addOffsetAndValueToEntries(offset uintptr, value int32, entries []PCDataEntry) []PCDataEntry { 178 var callbackPCDataEntries []PCDataEntry 179 180 for _, callbackPCInfo := range entries { 181 callbackPCDataEntries = append(callbackPCDataEntries, PCDataEntry{offset + callbackPCInfo.Offset, value + callbackPCInfo.Value}) 182 } 183 184 return callbackPCDataEntries 185 } 186 187 188 func addCallbacksEntries(pcDataEntries []PCDataEntry, offsetMappings []AddressMapping, pcDataGenerator func(uintptr, uintptr, int32) ([]PCDataEntry, error)) ([]PCDataEntry, error) { 189 for mapIndex, mapping := range offsetMappings { 190 if _, ok := CallbacksMarkers[mapping.OriginalAddress]; ok { 191 callbackOffsetStart := mapping.NewAddress 192 callbackOffsetEnd := offsetMappings[mapIndex+1].NewAddress 193 newPCDataEntries, err := addCallbackEntry(pcDataEntries, callbackOffsetStart, callbackOffsetEnd, pcDataGenerator) 194 if err != nil { 195 return nil, err 196 } 197 pcDataEntries = newPCDataEntries 198 } 199 } 200 201 return pcDataEntries, nil 202 } 203 204 type PCDataPatcher struct { 205 newFuncEntry uintptr 206 offsetMappings []AddressMapping 207 isPatched bool 208 instSizeReader func(pc uintptr) (uintptr, rookoutErrors.RookoutError) 209 } 210 211 212 213 214 215 216 217 218 func verifyOffsetMappings(offsetMappings []AddressMapping) rookoutErrors.RookoutError { 219 numLastMappingsToCheck := 2 220 if len(offsetMappings) < numLastMappingsToCheck { 221 return rookoutErrors.NewIllegalAddressMappings() 222 } 223 lastMappings := offsetMappings[len(offsetMappings)-numLastMappingsToCheck:] 224 for _, m := range lastMappings { 225 if _, ok := CallbacksMarkers[m.OriginalAddress]; ok { 226 return rookoutErrors.NewIllegalAddressMappings() 227 } 228 } 229 230 return nil 231 } 232 233 func NewPCDataPatcher(newFuncEntry uintptr, offsetMappings []AddressMapping, isPatched bool, instSizeReader func(uintptr) (uintptr, rookoutErrors.RookoutError)) (*PCDataPatcher, error) { 234 if err := verifyOffsetMappings(offsetMappings); err != nil { 235 return nil, err 236 } 237 return &PCDataPatcher{ 238 newFuncEntry: newFuncEntry, 239 offsetMappings: offsetMappings, 240 isPatched: isPatched, 241 instSizeReader: instSizeReader, 242 }, nil 243 } 244 245 func (p *PCDataPatcher) updateOffsets(table []PCDataEntry) error { 246 updatePCDataEntries(table, p.offsetMappings) 247 248 249 250 251 252 253 254 255 256 257 258 var newMappings []AddressMapping 259 for i := 0; i < len(p.offsetMappings); i++ { 260 currentMapping := p.offsetMappings[i] 261 if _, ok := CallbacksMarkers[currentMapping.OriginalAddress]; ok { 262 i++ 263 264 for ; i < len(p.offsetMappings); i++ { 265 if _, ok := CallbacksMarkers[p.offsetMappings[i].OriginalAddress]; !ok { 266 break 267 } 268 } 269 if i < len(p.offsetMappings) { 270 newMappings = append(newMappings, AddressMapping{OriginalAddress: p.offsetMappings[i].NewAddress, NewAddress: currentMapping.NewAddress}) 271 } else { 272 273 return rookoutErrors.NewIllegalAddressMappings() 274 } 275 } 276 } 277 updatePCDataEntries(table, newMappings) 278 279 return nil 280 } 281 282 func (p *PCDataPatcher) createSanitized(oldTable []PCDataEntry, builder func([]PCDataEntry) ([]PCDataEntry, error)) ([]PCDataEntry, error) { 283 var oldTableCopy []PCDataEntry = nil 284 if len(oldTable) > 0 { 285 oldTableCopy = make([]PCDataEntry, len(oldTable)) 286 copy(oldTableCopy, oldTable) 287 } 288 289 newTable, err := builder(oldTableCopy) 290 if err != nil { 291 return nil, err 292 } 293 return removeDuplicateValues(newTable), nil 294 } 295 296 297 298 299 func (p *PCDataPatcher) CreatePCData(tableIndex int, oldTable []PCDataEntry) ([]PCDataEntry, error) { 300 return p.createSanitized(oldTable, 301 func(table []PCDataEntry) ([]PCDataEntry, error) { 302 err := p.updateOffsets(table) 303 if err != nil { 304 return nil, err 305 } 306 307 if p.isPatched && tableIndex == _PCDATA_UnsafePoint { 308 table, err = p.fixAsyncUnsafePointPCData(table) 309 } 310 311 return table, err 312 }) 313 } 314 315 316 func (p *PCDataPatcher) CreatePCLine(oldTable []PCDataEntry) ([]PCDataEntry, error) { 317 return p.createSanitized(oldTable, 318 func(table []PCDataEntry) ([]PCDataEntry, error) { 319 err := p.updateOffsets(table) 320 return table, err 321 }) 322 } 323 324 325 326 func (p *PCDataPatcher) CreatePCSP(oldTable []PCDataEntry) ([]PCDataEntry, error) { 327 return p.createSanitized(oldTable, 328 func(table []PCDataEntry) ([]PCDataEntry, error) { 329 err := p.updateOffsets(table) 330 if err != nil { 331 return nil, err 332 } 333 if p.isPatched { 334 pcspGenerator := func(callbackOffsetStart, callbackOffsetEnd uintptr, callbackOffsetValue int32) ([]PCDataEntry, error) { 335 336 patchedPCSP, err := generatePCSP(callbackOffsetStart+p.newFuncEntry, callbackOffsetEnd+p.newFuncEntry) 337 if err != nil { 338 return nil, err 339 } 340 341 return addOffsetAndValueToEntries(callbackOffsetStart, callbackOffsetValue, patchedPCSP), nil 342 } 343 table, err = addCallbacksEntries(table, p.offsetMappings, pcspGenerator) 344 if err != nil { 345 return nil, err 346 } 347 } 348 return table, nil 349 }) 350 } 351 352 353 354 355 356 357 358 359 func (p *PCDataPatcher) fixAsyncUnsafePointPCData(entries []PCDataEntry) ([]PCDataEntry, error) { 360 if len(entries) == 0 { 361 entries = []PCDataEntry{{Offset: p.offsetMappings[len(p.offsetMappings)-1].NewAddress, Value: _PCDATA_UnsafePointSafe}} 362 } 363 364 pcdataAsyncUnsafeGenerator := func(callbackOffsetStart, callbackOffsetEnd uintptr, callbackOffsetValue int32) ([]PCDataEntry, error) { 365 firstCallbackInstructionPC := callbackOffsetStart + p.newFuncEntry 366 firstCallbackInstructionSize, err := p.instSizeReader(firstCallbackInstructionPC) 367 if err != nil { 368 return nil, err 369 } 370 callbackEntries := []PCDataEntry{ 371 { 372 373 Offset: callbackOffsetStart + firstCallbackInstructionSize, 374 Value: callbackOffsetValue, 375 }, 376 { 377 378 Offset: callbackOffsetEnd, 379 Value: _PCDATA_UnsafePointUnsafe, 380 }, 381 } 382 return callbackEntries, nil 383 } 384 return addCallbacksEntries(entries, p.offsetMappings, pcdataAsyncUnsafeGenerator) 385 }