test_vfs.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Test the VFS to exhaustion, specifically looking for deadlocks
  2. //
  3. // Run on a mounted filesystem
  4. package main
  5. import (
  6. "flag"
  7. "fmt"
  8. "io"
  9. "log"
  10. "math"
  11. "math/rand"
  12. "os"
  13. "path"
  14. "sync"
  15. "sync/atomic"
  16. "time"
  17. "github.com/rclone/rclone/lib/file"
  18. "github.com/rclone/rclone/lib/random"
  19. )
  20. var (
  21. nameLength = flag.Int("name-length", 10, "Length of names to create")
  22. verbose = flag.Bool("v", false, "Set to show more info")
  23. number = flag.Int("n", 4, "Number of tests to run simultaneously")
  24. iterations = flag.Int("i", 100, "Iterations of the test")
  25. timeout = flag.Duration("timeout", 10*time.Second, "Inactivity time to detect a deadlock")
  26. testNumber atomic.Int32
  27. )
  28. // Test contains stats about the running test which work for files or
  29. // directories
  30. type Test struct {
  31. dir string
  32. name string
  33. created bool
  34. handle *os.File
  35. tests []func()
  36. isDir bool
  37. number int32
  38. prefix string
  39. timer *time.Timer
  40. }
  41. // NewTest creates a new test and fills in the Tests
  42. func NewTest(Dir string) *Test {
  43. t := &Test{
  44. dir: Dir,
  45. name: random.String(*nameLength),
  46. isDir: rand.Intn(2) == 0,
  47. number: testNumber.Add(1),
  48. timer: time.NewTimer(*timeout),
  49. }
  50. width := int(math.Floor(math.Log10(float64(*number)))) + 1
  51. t.prefix = fmt.Sprintf("%*d: %s: ", width, t.number, t.path())
  52. if t.isDir {
  53. t.tests = []func(){
  54. t.list,
  55. t.rename,
  56. t.mkdir,
  57. t.rmdir,
  58. }
  59. } else {
  60. t.tests = []func(){
  61. t.list,
  62. t.rename,
  63. t.open,
  64. t.close,
  65. t.remove,
  66. t.read,
  67. t.write,
  68. }
  69. }
  70. return t
  71. }
  72. // kick the deadlock timeout
  73. func (t *Test) kick() {
  74. if !t.timer.Stop() {
  75. <-t.timer.C
  76. }
  77. t.timer.Reset(*timeout)
  78. }
  79. // randomTest runs a random test
  80. func (t *Test) randomTest() {
  81. t.kick()
  82. i := rand.Intn(len(t.tests))
  83. t.tests[i]()
  84. }
  85. // logf logs things - not shown unless -v
  86. func (t *Test) logf(format string, a ...interface{}) {
  87. if *verbose {
  88. log.Printf(t.prefix+format, a...)
  89. }
  90. }
  91. // errorf logs errors
  92. func (t *Test) errorf(format string, a ...interface{}) {
  93. log.Printf(t.prefix+"ERROR: "+format, a...)
  94. }
  95. // list test
  96. func (t *Test) list() {
  97. t.logf("list")
  98. fis, err := os.ReadDir(t.dir)
  99. if err != nil {
  100. t.errorf("%s: failed to read directory: %v", t.dir, err)
  101. return
  102. }
  103. if t.created && len(fis) == 0 {
  104. t.errorf("%s: expecting entries in directory, got none", t.dir)
  105. return
  106. }
  107. found := false
  108. for _, fi := range fis {
  109. if fi.Name() == t.name {
  110. found = true
  111. }
  112. }
  113. if t.created {
  114. if !found {
  115. t.errorf("%s: expecting to find %q in directory, got none", t.dir, t.name)
  116. return
  117. }
  118. } else {
  119. if found {
  120. t.errorf("%s: not expecting to find %q in directory, got none", t.dir, t.name)
  121. return
  122. }
  123. }
  124. }
  125. // path returns the current path to the item
  126. func (t *Test) path() string {
  127. return path.Join(t.dir, t.name)
  128. }
  129. // rename test
  130. func (t *Test) rename() {
  131. if !t.created {
  132. return
  133. }
  134. t.logf("rename")
  135. NewName := random.String(*nameLength)
  136. newPath := path.Join(t.dir, NewName)
  137. err := os.Rename(t.path(), newPath)
  138. if err != nil {
  139. t.errorf("failed to rename to %q: %v", newPath, err)
  140. return
  141. }
  142. t.name = NewName
  143. }
  144. // close test
  145. func (t *Test) close() {
  146. if t.handle == nil {
  147. return
  148. }
  149. t.logf("close")
  150. err := t.handle.Close()
  151. t.handle = nil
  152. if err != nil {
  153. t.errorf("failed to close: %v", err)
  154. return
  155. }
  156. }
  157. // open test
  158. func (t *Test) open() {
  159. t.close()
  160. t.logf("open")
  161. handle, err := file.OpenFile(t.path(), os.O_RDWR|os.O_CREATE, 0666)
  162. if err != nil {
  163. t.errorf("failed to open: %v", err)
  164. return
  165. }
  166. t.handle = handle
  167. t.created = true
  168. }
  169. // read test
  170. func (t *Test) read() {
  171. if t.handle == nil {
  172. return
  173. }
  174. t.logf("read")
  175. bytes := make([]byte, 10)
  176. _, err := t.handle.Read(bytes)
  177. if err != nil && err != io.EOF {
  178. t.errorf("failed to read: %v", err)
  179. return
  180. }
  181. }
  182. // write test
  183. func (t *Test) write() {
  184. if t.handle == nil {
  185. return
  186. }
  187. t.logf("write")
  188. bytes := make([]byte, 10)
  189. _, err := t.handle.Write(bytes)
  190. if err != nil {
  191. t.errorf("failed to write: %v", err)
  192. return
  193. }
  194. }
  195. // remove test
  196. func (t *Test) remove() {
  197. if !t.created {
  198. return
  199. }
  200. t.logf("remove")
  201. err := os.Remove(t.path())
  202. if err != nil {
  203. t.errorf("failed to remove: %v", err)
  204. return
  205. }
  206. t.created = false
  207. }
  208. // mkdir test
  209. func (t *Test) mkdir() {
  210. if t.created {
  211. return
  212. }
  213. t.logf("mkdir")
  214. err := os.Mkdir(t.path(), 0777)
  215. if err != nil {
  216. t.errorf("failed to mkdir %q", t.path())
  217. return
  218. }
  219. t.created = true
  220. }
  221. // rmdir test
  222. func (t *Test) rmdir() {
  223. if !t.created {
  224. return
  225. }
  226. t.logf("rmdir")
  227. err := os.Remove(t.path())
  228. if err != nil {
  229. t.errorf("failed to rmdir %q", t.path())
  230. return
  231. }
  232. t.created = false
  233. }
  234. // Tidy removes any stray files and stops the deadlock timer
  235. func (t *Test) Tidy() {
  236. t.timer.Stop()
  237. if !t.isDir {
  238. t.close()
  239. t.remove()
  240. } else {
  241. t.rmdir()
  242. }
  243. t.logf("finished")
  244. }
  245. // RandomTests runs random tests with deadlock detection
  246. func (t *Test) RandomTests(iterations int, quit chan struct{}) {
  247. var finished = make(chan struct{})
  248. go func() {
  249. for i := 0; i < iterations; i++ {
  250. t.randomTest()
  251. }
  252. close(finished)
  253. }()
  254. select {
  255. case <-finished:
  256. case <-quit:
  257. quit <- struct{}{}
  258. case <-t.timer.C:
  259. t.errorf("deadlock detected")
  260. quit <- struct{}{}
  261. }
  262. }
  263. func main() {
  264. flag.Parse()
  265. args := flag.Args()
  266. if len(args) != 1 {
  267. log.Fatalf("%s: Syntax [opts] <directory>", os.Args[0])
  268. }
  269. dir := args[0]
  270. _ = file.MkdirAll(dir, 0777)
  271. var (
  272. wg sync.WaitGroup
  273. quit = make(chan struct{}, *iterations)
  274. )
  275. for i := 0; i < *number; i++ {
  276. wg.Add(1)
  277. go func() {
  278. defer wg.Done()
  279. t := NewTest(dir)
  280. defer t.Tidy()
  281. t.RandomTests(*iterations, quit)
  282. }()
  283. }
  284. wg.Wait()
  285. }