profiles.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. filenameSuffix string,
  39. blockSampleDurationSeconds int,
  40. cpuSampleDurationSeconds int) {
  41. openProfileFile := func(profileName string) *os.File {
  42. filename := filepath.Join(outputDirectory, profileName+".profile")
  43. if filenameSuffix != "" {
  44. filename += "." + filenameSuffix
  45. }
  46. file, err := os.OpenFile(
  47. filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
  48. if err != nil {
  49. logger.WithTraceFields(
  50. LogFields{
  51. "error": err,
  52. "fileName": filename}).Error("open profile file failed")
  53. return nil
  54. }
  55. return file
  56. }
  57. writeProfile := func(profileName string) {
  58. file := openProfileFile(profileName)
  59. if file == nil {
  60. return
  61. }
  62. err := pprof.Lookup(profileName).WriteTo(file, 1)
  63. file.Close()
  64. if err != nil {
  65. logger.WithTraceFields(
  66. LogFields{
  67. "error": err,
  68. "profileName": profileName}).Error("write profile failed")
  69. }
  70. }
  71. // TODO: capture https://golang.org/pkg/runtime/debug/#WriteHeapDump?
  72. // May not be useful in its current state, as per:
  73. // https://groups.google.com/forum/#!topic/golang-dev/cYAkuU45Qyw
  74. // Write goroutine, heap, and threadcreate profiles
  75. // https://golang.org/pkg/runtime/pprof/#Profile
  76. writeProfile("goroutine")
  77. writeProfile("heap")
  78. writeProfile("threadcreate")
  79. // Write CPU profile (after sampling)
  80. // https://golang.org/pkg/runtime/pprof/#StartCPUProfile
  81. if cpuSampleDurationSeconds > 0 {
  82. file := openProfileFile("cpu")
  83. if file != nil {
  84. logger.WithTrace().Info("start cpu profiling")
  85. err := pprof.StartCPUProfile(file)
  86. if err != nil {
  87. logger.WithTraceFields(
  88. LogFields{"error": err}).Error("StartCPUProfile failed")
  89. } else {
  90. time.Sleep(time.Duration(cpuSampleDurationSeconds) * time.Second)
  91. pprof.StopCPUProfile()
  92. logger.WithTrace().Info("end cpu profiling")
  93. }
  94. file.Close()
  95. }
  96. }
  97. // Write block profile (after sampling)
  98. // https://golang.org/pkg/runtime/pprof/#Profile
  99. if blockSampleDurationSeconds > 0 {
  100. logger.WithTrace().Info("start block/mutex profiling")
  101. runtime.SetBlockProfileRate(1)
  102. runtime.SetMutexProfileFraction(1)
  103. time.Sleep(time.Duration(blockSampleDurationSeconds) * time.Second)
  104. runtime.SetBlockProfileRate(0)
  105. runtime.SetMutexProfileFraction(0)
  106. logger.WithTrace().Info("end block/mutex profiling")
  107. writeProfile("block")
  108. writeProfile("mutex")
  109. }
  110. }