main.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "path"
  20. "syscall"
  21. "time"
  22. "github.com/sevlyar/go-daemon"
  23. "golang.org/x/sys/unix"
  24. "yunion.io/x/log"
  25. "yunion.io/x/log/hooks"
  26. "yunion.io/x/pkg/errors"
  27. "yunion.io/x/pkg/util/signalutils"
  28. "yunion.io/x/pkg/utils"
  29. "yunion.io/x/structarg"
  30. "yunion.io/x/onecloud/pkg/util/fileutils2"
  31. "yunion.io/x/onecloud/pkg/util/procutils"
  32. )
  33. const (
  34. MEM_BACKEND_FD = "/memfd:memory-backend-memfd (deleted)"
  35. MMAP_SIZE = 2 * 1024 * 1024 * 1024
  36. )
  37. type Options struct {
  38. Pid int `help:"qemu process pid" required:"true"`
  39. MemSize int64 `help:"backend memory size" required:"true"`
  40. Foreground bool `help:"run in foreground"`
  41. LogDir string `help:"log dir" required:"true"`
  42. }
  43. var opt = &Options{}
  44. func main() {
  45. procDir := fmt.Sprintf("/proc/%d", opt.Pid)
  46. if !fileutils2.IsDir(procDir) {
  47. log.Fatalf("Process %d not found", opt.Pid)
  48. }
  49. fdDir := fmt.Sprintf("%s/fd", procDir)
  50. memBackendFd, err := findMemBackendFd(fdDir)
  51. if err != nil {
  52. log.Fatalf("findMemBackendFd: %s", err)
  53. }
  54. log.Infof("found mem backend fd: %s", memBackendFd)
  55. f, err := os.OpenFile(memBackendFd, os.O_RDWR, 0644)
  56. if err != nil {
  57. log.Fatalf("failed open memfd: %s", err)
  58. }
  59. defer f.Close()
  60. if !opt.Foreground {
  61. cntxt := &daemon.Context{
  62. WorkDir: "./",
  63. Umask: 027,
  64. }
  65. d, err := cntxt.Reborn()
  66. if err != nil {
  67. log.Fatalf("Unable to run in background: %s", err)
  68. }
  69. if d != nil {
  70. return
  71. }
  72. defer cntxt.Release()
  73. }
  74. log.Infof("start watch proc exit")
  75. err = procutils.NewCommand("tail", fmt.Sprintf("--pid=%d", opt.Pid), "-f", "/dev/null").Run()
  76. if err != nil {
  77. log.Fatalf("failed watch process: %s", err)
  78. }
  79. log.Infof("watch proc exited, go to clean memory")
  80. var (
  81. start = time.Now()
  82. size int64 = 0
  83. )
  84. for size < opt.MemSize {
  85. mmapSize := MMAP_SIZE
  86. if opt.MemSize-size < MMAP_SIZE {
  87. mmapSize = int(opt.MemSize - size)
  88. }
  89. b, err := syscall.Mmap(
  90. int(f.Fd()), size, mmapSize,
  91. syscall.PROT_WRITE|syscall.PROT_READ,
  92. syscall.MAP_SHARED,
  93. )
  94. if err != nil {
  95. log.Fatalf("failed mmap mem backend fd: %s", err)
  96. }
  97. log.Infof("mmap memory offset %d, size %d", size, len(b))
  98. size += int64(len(b))
  99. // memsetRepeat(b, 0)
  100. for i := 0; i < len(b); i++ {
  101. b[i] = 0
  102. }
  103. unix.Msync(b, unix.MS_SYNC)
  104. syscall.Munmap(b)
  105. }
  106. log.Infof(
  107. "mem clean for process %d success, mem clean took %s",
  108. opt.Pid, time.Since(start),
  109. )
  110. }
  111. func findMemBackendFd(fdDir string) (string, error) {
  112. files, err := ioutil.ReadDir(fdDir)
  113. if err != nil {
  114. return "", errors.Wrapf(err, "read dir %s", fdDir)
  115. }
  116. for _, f := range files {
  117. p1 := path.Join(fdDir, f.Name())
  118. p2, e := os.Readlink(p1)
  119. if e != nil {
  120. log.Errorf("os.readlink %s: %s", p1, e)
  121. continue
  122. }
  123. log.Infof("os.readlink path %s", p2)
  124. if p2 == MEM_BACKEND_FD {
  125. return p1, nil
  126. }
  127. }
  128. return "", errors.Errorf("no mem backend fd found")
  129. }
  130. func memsetRepeat(a []byte, v byte) {
  131. if len(a) == 0 {
  132. return
  133. }
  134. a[0] = v
  135. for bp := 1; bp < len(a); bp *= 2 {
  136. copy(a[bp:], a[:bp])
  137. }
  138. }
  139. func init() {
  140. signalutils.RegisterSignal(func() {
  141. utils.DumpAllGoroutineStack(log.Logger().Out)
  142. }, syscall.SIGUSR1)
  143. signalutils.StartTrap()
  144. parser, err := structarg.NewArgumentParser(opt, "", "", "")
  145. if err != nil {
  146. log.Fatalf("Error define argument parser: %v", err)
  147. }
  148. err = parser.ParseArgs2(os.Args[1:], true, true)
  149. if err != nil {
  150. log.Fatalf("Failed parse args %s", err)
  151. }
  152. logFileHook := hooks.LogFileRotateHook{
  153. LogFileHook: hooks.LogFileHook{
  154. FileDir: opt.LogDir,
  155. FileName: "memclean.log",
  156. },
  157. RotateNum: 10,
  158. RotateSize: 100 * 1024 * 1024,
  159. }
  160. logFileHook.Init()
  161. log.Logger().AddHook(&logFileHook)
  162. }