Skip to content

Commit

Permalink
Merge pull request #30 from parca-dev/add_tls_and_312_support
Browse files Browse the repository at this point in the history
python: Add TLS, 3.12 and 3.13 Offsets
  • Loading branch information
kakkoyun authored Feb 23, 2024
2 parents ac2e144 + a9543ef commit 6f36c4f
Show file tree
Hide file tree
Showing 53 changed files with 532 additions and 122 deletions.
31 changes: 17 additions & 14 deletions cmd/structlayout/structlayout.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,15 @@ func main() {

var (
layoutMap runtimedata.LayoutMap
initialStateMap any
initialStateMap runtimedata.InitialStateMap
outputDir = givenOutputDir
)
switch runtime {
case "python":
if strings.Contains(version, "a") {
// Alpha version detected.
version = strings.ReplaceAll(version, "a", "-alpha.")
}
layoutMap = python.DataMapForLayout(version)
initialStateMap = python.DataMapForInitialState(version)
if outputDir == "" {
Expand All @@ -89,11 +93,6 @@ func main() {
os.Exit(1)
}

if layoutMap == nil {
logger.Error("unknown version", "version", version)
os.Exit(1)
}

var (
input = fSet.Arg(0)
)
Expand All @@ -103,19 +102,23 @@ func main() {
os.Exit(1)
}

output := filepath.Join(outputDir, "layout", fmt.Sprintf("%s_%s.yaml", runtime, sanitizeIdentifier(version)))
if err := processAndWriteLayout(dwarfData, output, version, layoutMap); err != nil {
logger.Error("failed to write layout", "err", err)
os.Exit(1)
if !isNil(layoutMap) {
output := filepath.Join(outputDir, "layout", fmt.Sprintf("%s_%s.yaml", runtime, sanitizeIdentifier(version)))
if err := processAndWriteLayout(dwarfData, output, version, layoutMap); err != nil {
logger.Error("failed to write layout", "err", err)
os.Exit(1)
}
logger.Info("layout file written", "file", output)
} else {
logger.Info("no layout map found, skipping layout generation")
}
logger.Info("layout file written", "file", output)

if isNil(initialStateMap) {
logger.Info("no initial state map found, skipping initial state generation")
os.Exit(0)
}

output = filepath.Join(outputDir, "initialstate", fmt.Sprintf("%s_%s.yaml", runtime, sanitizeIdentifier(version)))
output := filepath.Join(outputDir, "initialstate", fmt.Sprintf("%s_%s.yaml", runtime, sanitizeIdentifier(version)))
if err := processAndWriteInitialState(dwarfData, output, version, initialStateMap); err != nil {
logger.Error("failed to write initial state", "err", err)
os.Exit(1)
Expand Down Expand Up @@ -161,7 +164,7 @@ func processAndWriteLayout(dwarfData *dwarf.Data, output string, version string,
}

// processAndWriteInitialState processes the given ELF file and writes the initial state to the given output file.
func processAndWriteInitialState(dwarfData *dwarf.Data, output string, version string, initialStateMap any) error {
func processAndWriteInitialState(dwarfData *dwarf.Data, output string, version string, initialStateMap runtimedata.InitialStateMap) error {
dm, err := datamap.New(initialStateMap)
if err != nil {
return fmt.Errorf("failed to create data map: %w", err)
Expand All @@ -181,7 +184,7 @@ func processAndWriteInitialState(dwarfData *dwarf.Data, output string, version s
}

// Extremely in-efficient and hacky but it should work for now.
withVersion, err := runtimedata.WithVersion(version, convertToMapOfAny(initialStateMap))
withVersion, err := runtimedata.WithVersion(version, convertToMapOfAny(initialStateMap.InitialState()))
if err != nil {
return fmt.Errorf("failed to wrap layout with version: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/libc/glibc/datamap.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ type glibc struct {

func (g *glibc) Layout() runtimedata.RuntimeData {
return &Layout{
PthreadSpecific1stblock: g.PThreadSpecific1stblock,
PthreadSize: g.PThreadSize,
PthreadKeyData: g.PThreadKeyData,
PthreadKeyDataSize: g.PThreadKeyDataSize,
PThreadSpecific1stblock: g.PThreadSpecific1stblock,
PThreadSize: g.PThreadSize,
PThreadKeyData: g.PThreadKeyData,
PThreadKeyDataSize: g.PThreadKeyDataSize,
}
}

Expand Down
32 changes: 16 additions & 16 deletions pkg/libc/glibc/glibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,43 @@ func Test_getLayoutForArch(t *testing.T) {
v: semver.MustParse("2.29.0"),
arch: "amd64",
want: &Layout{
PthreadSpecific1stblock: 784,
PthreadSize: 2304,
PthreadKeyData: 8,
PthreadKeyDataSize: 16,
PThreadSpecific1stblock: 784,
PThreadSize: 2304,
PThreadKeyData: 0x08,
PThreadKeyDataSize: 0x10,
},
},
{
name: "2.29.0",
v: semver.MustParse("2.29.0"),
arch: "arm64",
want: &Layout{
PthreadSpecific1stblock: 272,
PthreadSize: 1792,
PthreadKeyData: 8,
PthreadKeyDataSize: 16,
PThreadSpecific1stblock: 272,
PThreadSize: 1792,
PThreadKeyData: 0x08,
PThreadKeyDataSize: 0x10,
},
},
{
name: "2.37.0",
v: semver.MustParse("2.37.0"),
arch: "amd64",
want: &Layout{
PthreadSpecific1stblock: 784,
PthreadSize: 2368,
PthreadKeyData: 8,
PthreadKeyDataSize: 16,
PThreadSpecific1stblock: 784,
PThreadSize: 2368,
PThreadKeyData: 0x08,
PThreadKeyDataSize: 0x10,
},
},
{
name: "2.37.0",
v: semver.MustParse("2.37.0"),
arch: "arm64",
want: &Layout{
PthreadSpecific1stblock: 272,
PthreadSize: 1856,
PthreadKeyData: 8,
PthreadKeyDataSize: 16,
PThreadSpecific1stblock: 272,
PThreadSize: 1856,
PThreadKeyData: 0x08,
PThreadKeyDataSize: 0x10,
},
},
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/libc/glibc/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
)

type Layout struct {
PthreadSpecific1stblock int64 `yaml:"pthread_specific_1stblock"`
PthreadSize int64 `yaml:"pthread_size"`
PthreadKeyData int64 `yaml:"pthread_key_data"`
PthreadKeyDataSize int64 `yaml:"pthread_key_data_size"`
PThreadSpecific1stblock int64 `yaml:"pthread_specific_1stblock"`
PThreadSize int64 `yaml:"pthread_size"`
PThreadKeyData int64 `yaml:"pthread_key_data"`
PThreadKeyDataSize int64 `yaml:"pthread_key_data_size"`
}

func (l Layout) Data() ([]byte, error) {
Expand Down
71 changes: 71 additions & 0 deletions pkg/python/datamap.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func DataMapForLayout(v string) runtimedata.LayoutMap {
version.MustParseConstraints("3.3.x - 3.9.x"): &python33_39{},
version.MustParseConstraints("3.10.x"): &python310{},
version.MustParseConstraints("3.11.x"): &python311{},
version.MustParseConstraints("3.12.x"): &python312{},
}

lookupVersion := semver.MustParse(v)
Expand Down Expand Up @@ -302,3 +303,73 @@ func (p python311) Layout() runtimedata.RuntimeData {
},
}
}

type python312 struct {
PyObjectObType int64 `offsetof:"PyObject.ob_type"`
PyStringData int64 `sizeof:"PyASCIIObject"`
PyTypeObjectTpName int64 `offsetof:"PyTypeObject.tp_name"`
PyThreadStateInterp int64 `offsetof:"PyThreadState.interp"`
PyThreadStateNext int64 `offsetof:"PyThreadState.next"`
PyThreadStateThreadID int64 `offsetof:"PyThreadState.thread_id"`
PyThreadStateNativeThreadID int64 `offsetof:"PyThreadState.native_thread_id"`
PyThreadStateCFrame int64 `offsetof:"PyThreadState.cframe"`
PyInterpreterStateTstateHead int64 `offsetof:"PyInterpreterState.threads"`
PyInterpreterStateIsPythreadsHead int64 `offsetof:"pythreads.head"`
PyRuntimeStateInterpreters int64 `offsetof:"pyruntimestate.interpreters"`
PyRuntimeStatePyInterpretersMain int64 `offsetof:"pyinterpreters.main"`
PyFrameObjectFBack int64 `offsetof:"_PyInterpreterFrame.previous"`
PyFrameObjectFCode int64 `offsetof:"_PyInterpreterFrame.f_code"`
PyFrameObjectFLocalsplus int64 `offsetof:"_PyInterpreterFrame.localsplus"`
PyCodeObjectCoFilename int64 `offsetof:"PyCodeObject.co_filename"`
PyCodeObjectCoName int64 `offsetof:"PyCodeObject.co_name"`
PyCodeObjectCoVarNames int64 `offsetof:"PyCodeObject.co_localsplusnames"`
PyCodeObjectCoFirstlineno int64 `offsetof:"PyCodeObject.co_firstlineno"`
PyTupleObjectObItem int64 `offsetof:"PyTupleObject.ob_item"`
}

func (p python312) Layout() runtimedata.RuntimeData {
return &Layout{
PyObject: PyObject{
ObType: p.PyObjectObType,
},
PyString: PyString{
Data: p.PyStringData,
Size: doesNotExist,
},
PyTypeObject: PyTypeObject{
TPName: p.PyTypeObjectTpName,
},
PyThreadState: PyThreadState{
Interp: p.PyThreadStateInterp,
Next: p.PyThreadStateNext,
Frame: doesNotExist,
ThreadID: p.PyThreadStateThreadID,
NativeThreadID: p.PyThreadStateNativeThreadID,
CFrame: p.PyThreadStateCFrame,
},
PyCFrame: PyCFrame{
CurrentFrame: doesNotExist,
},
PyInterpreterState: PyInterpreterState{
TStateHead: p.PyInterpreterStateTstateHead + p.PyInterpreterStateIsPythreadsHead,
},
PyRuntimeState: PyRuntimeState{
InterpMain: p.PyRuntimeStateInterpreters + p.PyRuntimeStatePyInterpretersMain,
},
PyFrameObject: PyFrameObject{
FBack: p.PyFrameObjectFBack,
FCode: p.PyFrameObjectFCode,
FLineno: doesNotExist,
FLocalsplus: p.PyFrameObjectFLocalsplus,
},
PyCodeObject: PyCodeObject{
CoFilename: p.PyCodeObjectCoFilename,
CoName: p.PyCodeObjectCoName,
CoVarnames: p.PyCodeObjectCoVarNames,
CoFirstlineno: p.PyCodeObjectCoFirstlineno,
},
PyTupleObject: PyTupleObject{
ObItem: p.PyTupleObjectObItem,
},
}
}
95 changes: 83 additions & 12 deletions pkg/python/initialstate.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
package python

import (
"bytes"
"encoding/binary"
"fmt"
"unsafe"

"github.com/Masterminds/semver/v3"

"github.com/parca-dev/runtime-data/pkg/byteorder"
"github.com/parca-dev/runtime-data/pkg/runtimedata"
"github.com/parca-dev/runtime-data/pkg/version"
)

type InitialState struct {
InterpreterHead int64 `offsetof:"_PyRuntimeState.interpreters.head" yaml:"interpreter_head"`
ThreadStateCurrent int64 `offsetof:"_PyRuntimeState.gilstate.tstate_current" yaml:"tstate_current"`
}

var above3_7 = version.MustParseConstraints(">=3.7.0")
var (
above3_7 = version.MustParseConstraints("3.7.x - 3.11.x")
above3_12 = version.MustParseConstraints(">=3.12.0-0")
)

func DataMapForInitialState(v string) *InitialState {
func DataMapForInitialState(v string) runtimedata.InitialStateMap {
lookupVersion := semver.MustParse(v)
if above3_7.Check(lookupVersion) {
return &InitialState{}
switch {
case above3_7.Check(lookupVersion):
return &initialState38{}
case above3_12.Check(lookupVersion):
return &initialState312{}
}
// There is no offsets to be extracted version below 3.7
// so we return nil
// The state kept in the static global variables,
// we read them from the memory directly.
// Check the agent for more details.
return nil
}

Expand All @@ -49,3 +51,72 @@ func WithVersion(version string, is InitialState) (InitialStateWithVersion, erro
InitialState: is,
}, nil
}

// The state kept in the static global variables,
// we read them from the memory directly.
// Check the agent for more details.
type InitialState struct {
InterpreterHead int64 `yaml:"interpreter_head"`
ThreadStateCurrent int64 `yaml:"tstate_current"`
AutoTSSKey int64 `yaml:"auto_tss_key"`
PyTSS PyTSSKey `yaml:"tss"`
}

type PyTSSKey struct {
Key int64 `yaml:"key"`
Size int64 `yaml:"size"`
}

func (i InitialState) Data() ([]byte, error) {
buf := new(bytes.Buffer)
buf.Grow(int(unsafe.Sizeof(&i)))

if err := binary.Write(buf, byteorder.GetHostByteOrder(), &i); err != nil {
return nil, err
}

return buf.Bytes(), nil
}

type initialState312 struct {
InterpreterHead int64 `offsetof:"_PyRuntimeState.interpreters.head"`
AutoTSSKey int64 `offsetof:"_PyRuntimeState.autoTSSkey"`
PyTSSKey int64 `offsetof:"_Py_tss_t._key"`
PyTSSSize int64 `sizeof:"_Py_tss_t"`
}

func (i initialState312) InitialState() runtimedata.RuntimeData {
return &InitialState{
InterpreterHead: i.InterpreterHead,
AutoTSSKey: i.AutoTSSKey,
// https://github.com/python/cpython/issues/103323
ThreadStateCurrent: doesNotExist,
PyTSS: PyTSSKey{
Key: i.PyTSSKey,
Size: i.PyTSSSize,
},
}
}

// https://pythondev.readthedocs.io/pystate.html
// Python 3.7: PyThreadState_GET() reads _PyThreadState_Current (atomic variable).
// Python 3.8: _PyThreadState_Current becomes _PyRuntime.gilstate.tstate_current.
type initialState38 struct {
InterpreterHead int64 `offsetof:"_PyRuntimeState.interpreters.head"`
ThreadStateCurrent int64 `offsetof:"_PyRuntimeState.gilstate.tstate_current"`
AutoTSSKey int64 `offsetof:"_PyRuntimeState.gilstate.autoTSSkey"`
PyTSSKey int64 `offsetof:"_Py_tss_t._key"`
PyTSSSize int64 `sizeof:"_Py_tss_t"`
}

func (i initialState38) InitialState() runtimedata.RuntimeData {
return &InitialState{
InterpreterHead: i.InterpreterHead,
ThreadStateCurrent: i.ThreadStateCurrent,
AutoTSSKey: i.AutoTSSKey,
PyTSS: PyTSSKey{
Key: i.PyTSSKey,
Size: i.PyTSSSize,
},
}
}
2 changes: 0 additions & 2 deletions pkg/python/initialstate/amd64/3.11.0 - 3.11.8.yaml

This file was deleted.

6 changes: 6 additions & 0 deletions pkg/python/initialstate/amd64/3.12.0 - 3.12.2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
auto_tss_key: 1544
interpreter_head: 40
tss:
key: 4
size: 8
tstate_current: -1
6 changes: 6 additions & 0 deletions pkg/python/initialstate/amd64/3.7.0 - 3.7.2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
auto_tss_key: 1416
interpreter_head: 24
tss:
key: 4
size: 8
tstate_current: 1392
2 changes: 0 additions & 2 deletions pkg/python/initialstate/amd64/3.7.0 - 3.7.3.yaml

This file was deleted.

2 changes: 0 additions & 2 deletions pkg/python/initialstate/amd64/3.7.4 - 3.7.17.yaml

This file was deleted.

6 changes: 6 additions & 0 deletions pkg/python/initialstate/amd64/3.8.0 - 3.8.1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
auto_tss_key: 1392
interpreter_head: 32
tss:
key: 4
size: 8
tstate_current: 1368
Loading

0 comments on commit 6f36c4f

Please sign in to comment.