profiles.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /*
  2. * Copyright (c) 2018, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package common
  20. import (
  21. "os"
  22. "path/filepath"
  23. "runtime"
  24. "runtime/pprof"
  25. "time"
  26. )
  27. // WriteRuntimeProfiles writes Go runtime profile information to a set of
  28. // files in the specified output directory. The profiles include "heap",
  29. // "goroutine", and other selected profiles from:
  30. // https://golang.org/pkg/runtime/pprof/#Profile.
  31. //
  32. // The SampleDurationSeconds inputs determine how long to wait and sample
  33. // profiles that require active sampling. When set to 0, these profiles are
  34. // skipped.
  35. func WriteRuntimeProfiles(
  36. logger Logger,
  37. outputDirectory string,
  38. blockSampleDurationSeconds int,
  39. cpuSampleDurationSeconds int) {
  40. openProfileFile := func(profileName string) *os.File {
  41. fileName := filepath.Join(outputDirectory, profileName+".profile")
  42. file, err := os.OpenFile(
  43. fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
  44. if err != nil {
  45. logger.WithContextFields(
  46. LogFields{
  47. "error": err,
  48. "fileName": fileName}).Error("open profile file failed")
  49. return nil
  50. }
  51. return file
  52. }
  53. writeProfile := func(profileName string) {
  54. file := openProfileFile(profileName)
  55. if file == nil {
  56. return
  57. }
  58. err := pprof.Lookup(profileName).WriteTo(file, 1)
  59. file.Close()
  60. if err != nil {
  61. logger.WithContextFields(
  62. LogFields{
  63. "error": err,
  64. "profileName": profileName}).Error("write profile failed")
  65. }
  66. }
  67. // TODO: capture https://golang.org/pkg/runtime/debug/#WriteHeapDump?
  68. // May not be useful in its current state, as per:
  69. // https://groups.google.com/forum/#!topic/golang-dev/cYAkuU45Qyw
  70. // Write goroutine, heap, and threadcreate profiles
  71. // https://golang.org/pkg/runtime/pprof/#Profile
  72. writeProfile("goroutine")
  73. writeProfile("heap")
  74. writeProfile("threadcreate")
  75. // Write CPU profile (after sampling)
  76. // https://golang.org/pkg/runtime/pprof/#StartCPUProfile
  77. if cpuSampleDurationSeconds > 0 {
  78. file := openProfileFile("cpu")
  79. if file != nil {
  80. logger.WithContext().Info("start cpu profiling")
  81. err := pprof.StartCPUProfile(file)
  82. if err != nil {
  83. logger.WithContextFields(
  84. LogFields{"error": err}).Error("StartCPUProfile failed")
  85. } else {
  86. time.Sleep(time.Duration(cpuSampleDurationSeconds) * time.Second)
  87. pprof.StopCPUProfile()
  88. logger.WithContext().Info("end cpu profiling")
  89. }
  90. file.Close()
  91. }
  92. }
  93. // Write block profile (after sampling)
  94. // https://golang.org/pkg/runtime/pprof/#Profile
  95. if blockSampleDurationSeconds > 0 {
  96. logger.WithContext().Info("start block/mutex profiling")
  97. runtime.SetBlockProfileRate(1)
  98. runtime.SetMutexProfileFraction(1)
  99. time.Sleep(time.Duration(blockSampleDurationSeconds) * time.Second)
  100. runtime.SetBlockProfileRate(0)
  101. runtime.SetMutexProfileFraction(0)
  102. logger.WithContext().Info("end block/mutex profiling")
  103. writeProfile("block")
  104. writeProfile("mutex")
  105. }
  106. }