Skip to content
Draft
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
5 changes: 0 additions & 5 deletions runtime/internal/lib/runtime/pprof_linkname_llgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@ func pprof_goroutineLeakProfileWithLabels(p []StackRecord, labels []unsafe.Point
return 0, true
}

//go:linkname pprof_memProfileInternal runtime.pprof_memProfileInternal
func pprof_memProfileInternal(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
return 0, true
}

//go:linkname pprof_blockProfileInternal runtime.pprof_blockProfileInternal
func pprof_blockProfileInternal(p []BlockProfileRecord) (n int, ok bool) {
return 0, true
Expand Down
50 changes: 50 additions & 0 deletions runtime/internal/lib/runtime/pprof_memprofile_go123_llgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//go:build (darwin || linux) && go1.23

package runtime

import _ "unsafe"

type pprofMemProfileRecord struct {
AllocBytes, FreeBytes int64
AllocObjects, FreeObjects int64
Stack []uintptr
}

//go:linkname pprof_memProfileInternal runtime.pprof_memProfileInternal
func pprof_memProfileInternal(p []pprofMemProfileRecord, inuseZero bool) (n int, ok bool) {
n, _ = MemProfile(nil, inuseZero)
if len(p) < n {
return n, false
}
if n == 0 {
return 0, true
}
var records [64]MemProfileRecord
if n > len(records) {
return n, false
}
n, ok = MemProfile(records[:n], inuseZero)
if !ok {
return n, false
}
for i := 0; i < n; i++ {
p[i] = pprofMemProfileRecord{
AllocBytes: records[i].AllocBytes,
FreeBytes: records[i].FreeBytes,
AllocObjects: records[i].AllocObjects,
FreeObjects: records[i].FreeObjects,
Stack: pprofMemProfileStack(&records[i]),
}
}
return n, true
}

func pprofMemProfileStack(r *MemProfileRecord) []uintptr {
stack := r.Stack()
if len(stack) == 0 {
return nil
}
out := make([]uintptr, len(stack))
copy(out, stack)
return out
}
10 changes: 10 additions & 0 deletions runtime/internal/lib/runtime/pprof_memprofile_pre_go123_llgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build (darwin || linux) && !go1.23

package runtime

import _ "unsafe"

//go:linkname pprof_memProfileInternal runtime.pprof_memProfileInternal
func pprof_memProfileInternal(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
return MemProfile(p, inuseZero)
}
47 changes: 39 additions & 8 deletions runtime/internal/lib/runtime/pprof_runtime_stub_llgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

package runtime

// StackRecord is a minimal placeholder for runtime/pprof.
import llrt "github.com/goplus/llgo/runtime/internal/runtime"

type StackRecord struct {
Stack []uintptr
}

// MemProfileRecord is a minimal placeholder for runtime/pprof.
type MemProfileRecord struct {
AllocBytes int64
FreeBytes int64
AllocObjects int64
FreeObjects int64
Stack []uintptr
AllocBytes, FreeBytes int64
AllocObjects, FreeObjects int64
Stack0 [32]uintptr
}

func (r *MemProfileRecord) InUseBytes() int64 {
Expand All @@ -24,6 +22,15 @@ func (r *MemProfileRecord) InUseObjects() int64 {
return r.AllocObjects - r.FreeObjects
}

func (r *MemProfileRecord) Stack() []uintptr {
for i, pc := range r.Stack0 {
if pc == 0 {
return r.Stack0[:i]
}
}
return r.Stack0[:]
}

// BlockProfileRecord is a minimal placeholder for runtime/pprof.
type BlockProfileRecord struct {
Count int64
Expand All @@ -32,7 +39,31 @@ type BlockProfileRecord struct {
}

func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
return 0, false
n, _ = llrt.MemProfile(nil, inuseZero)
if len(p) < n {
return n, false
}
if n == 0 {
return 0, true
}
var records [64]llrt.MemProfileRecord
if n > len(records) {
return n, false
}
n, ok = llrt.MemProfile(records[:n], inuseZero)
if !ok {
return n, false
}
for i := 0; i < n; i++ {
p[i] = MemProfileRecord{
AllocBytes: records[i].AllocBytes,
FreeBytes: records[i].FreeBytes,
AllocObjects: records[i].AllocObjects,
FreeObjects: records[i].FreeObjects,
Stack0: records[i].Stack0,
}
}
return n, true
}

func BlockProfile(p []BlockProfileRecord) (n int, ok bool) {
Expand Down
112 changes: 112 additions & 0 deletions runtime/internal/runtime/memprofile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package runtime

// MemProfileRecord describes allocations aggregated by size class.
type MemProfileRecord struct {
AllocBytes, FreeBytes int64
AllocObjects, FreeObjects int64
Stack0 [32]uintptr
}

func (r *MemProfileRecord) InUseBytes() int64 {
return r.AllocBytes - r.FreeBytes
}

func (r *MemProfileRecord) InUseObjects() int64 {
return r.AllocObjects - r.FreeObjects
}

func (r *MemProfileRecord) Stack() []uintptr {
for i, pc := range r.Stack0 {
if pc == 0 {
return r.Stack0[:i]
}
}
return r.Stack0[:]
}

type memProfileBucket struct {
size uintptr

objects memProfileCounter
}

var memProfileBuckets = [...]memProfileBucket{
{size: 16},
{size: 32},
{size: 64},
{size: 128},
{size: 256},
{size: 512},
{size: 1024},
{size: 2048},
{size: 4096},
{size: 8192},
{size: 16384},
{size: 32768},
{size: 65536},
{size: 131072},
{size: 262144},
{size: 524288},
{size: 1048576},
{size: 2097152},
{size: 4194304},
{size: 8388608},
{size: 16777216},
{size: 33554432},
{size: 67108864},
{size: 134217728},
{size: 268435456},
{size: 536870912},
{size: 1073741824},
}

func recordMemProfileAlloc(size uintptr) {
if size == 0 {
return
}
size = memProfileSizeClass(size)
for i := range memProfileBuckets {
b := &memProfileBuckets[i]
if b.size == size {
memProfileAddObject(&b.objects)
return
}
}
}

func memProfileSizeClass(size uintptr) uintptr {
if size <= 16 {
return 16
}
for _, b := range memProfileBuckets {
if size <= b.size {
return b.size
}
}
return memProfileBuckets[len(memProfileBuckets)-1].size
}

func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
for i := range memProfileBuckets {
if memProfileLoadObjects(&memProfileBuckets[i].objects) != 0 {
n++
}
}
if len(p) < n {
return n, false
}
j := 0
for i := range memProfileBuckets {
b := &memProfileBuckets[i]
objects := memProfileLoadObjects(&b.objects)
if objects == 0 {
continue
}
p[j] = MemProfileRecord{
AllocBytes: int64(uint64(b.size) * uint64(objects)),
AllocObjects: int64(objects),
}
j++
}
return n, true
}
15 changes: 15 additions & 0 deletions runtime/internal/runtime/memprofile_atomic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !baremetal

package runtime

import "github.com/goplus/llgo/runtime/internal/clite/sync/atomic"

type memProfileCounter = uint64

func memProfileAddObject(p *memProfileCounter) {
atomic.Add(p, memProfileCounter(1))
}

func memProfileLoadObjects(p *memProfileCounter) memProfileCounter {
return atomic.Load(p)
}
13 changes: 13 additions & 0 deletions runtime/internal/runtime/memprofile_baremetal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build baremetal

package runtime

type memProfileCounter = uintptr

func memProfileAddObject(p *memProfileCounter) {
*p = *p + 1
}

func memProfileLoadObjects(p *memProfileCounter) memProfileCounter {
return *p
}
5 changes: 4 additions & 1 deletion runtime/internal/runtime/z_gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ import (

// AllocU allocates uninitialized memory.
func AllocU(size uintptr) unsafe.Pointer {
return bdwgc.Malloc(size)
ret := bdwgc.Malloc(size)
recordMemProfileAlloc(size)
return ret
}

// AllocZ allocates zero-initialized memory.
func AllocZ(size uintptr) unsafe.Pointer {
ret := bdwgc.Malloc(size)
recordMemProfileAlloc(size)
return c.Memset(ret, 0, size)
}

Expand Down
8 changes: 6 additions & 2 deletions runtime/internal/runtime/z_gc_baremetal.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ import (

// AllocU allocates uninitialized memory.
func AllocU(size uintptr) unsafe.Pointer {
return tinygogc.Alloc(size)
ret := tinygogc.Alloc(size)
recordMemProfileAlloc(size)
return ret
}

// AllocZ allocates zero-initialized memory.
func AllocZ(size uintptr) unsafe.Pointer {
return tinygogc.Alloc(size)
ret := tinygogc.Alloc(size)
recordMemProfileAlloc(size)
return ret
}

// AddCleanupPtr is not implemented in baremetal builds because tinygogc
Expand Down
5 changes: 4 additions & 1 deletion runtime/internal/runtime/z_nogc.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ import (

// AllocU allocates uninitialized memory.
func AllocU(size uintptr) unsafe.Pointer {
return c.Malloc(size)
ret := c.Malloc(size)
recordMemProfileAlloc(size)
return ret
}

// AllocZ allocates zero-initialized memory.
func AllocZ(size uintptr) unsafe.Pointer {
ret := c.Malloc(size)
recordMemProfileAlloc(size)
return c.Memset(ret, 0, size)
}

Expand Down
Loading
Loading