Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python: Add TLS, 3.12 and 3.13 Offsets #30

Merged
merged 2 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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