package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"runtime"
	"runtime/debug"
	"strconv"
	"strings"
	"time"
)

// link is an element of a linked list. It is larger than 64 kB, so the
// runtime creates a large allocation for it rather than packing it into a
// span.
type link struct {
	next *link
	buf  [512 << 10]byte
}

var list *link

func main() {
	var (
		limit = flag.Int("page-limit", 0, "limit on number of pages to use (0 for no limit)")
	)
	flag.Parse()

	var m runtime.MemStats
	f0, f1 := 0, 1
	sum := 0

	var done bool

	for i := 0; ; i++ {
		f0, f1 = f1, f0+f1

		for j := 0; j < f1; j++ {
			if *limit > 0 && sum >= *limit {
				done = true
				break
			}
			next := new(link)
			next.next = list
			list = next
			sum += 1
		}

		for {
			buf, _ := ioutil.ReadFile("/proc/self/stat")
			nums := make([]int64, 52+1)
			for i, val := range strings.Fields(string(buf)) {
				if i >= len(nums) {
					break
				}
				nums[i+1], _ = strconv.ParseInt(val, 10, 64)
			}

			var (
				minflt = nums[10]
				vsize  = nums[23]
				rss    = nums[24] * (4 << 10)
			)

			runtime.ReadMemStats(&m)

			fmt.Printf("HeapAlloc=%d TotalAlloc=%d minflt=%d vsize=%d rss=%d pages=%d\n",
				m.HeapAlloc, m.TotalAlloc, minflt, vsize, rss, sum)
			if !done {
				break
			}

			time.Sleep(1 * time.Second)
			debug.FreeOSMemory()
			debug.SetGCPercent(-1)
			for l := list; l != nil && l.next != nil; l = l.next {
				l.next = &link{next: l.next.next}
			}
			debug.FreeOSMemory()
		}

	}
}
