You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smat.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // +build gofuzz
  2. /*
  3. # Instructions for smat testing for roaring
  4. [smat](https://github.com/mschoch/smat) is a framework that provides
  5. state machine assisted fuzz testing.
  6. To run the smat tests for roaring...
  7. ## Prerequisites
  8. $ go get github.com/dvyukov/go-fuzz/go-fuzz
  9. $ go get github.com/dvyukov/go-fuzz/go-fuzz-build
  10. ## Steps
  11. 1. Generate initial smat corpus:
  12. ```
  13. go test -tags=gofuzz -run=TestGenerateSmatCorpus
  14. ```
  15. 2. Build go-fuzz test program with instrumentation:
  16. ```
  17. go-fuzz-build -func FuzzSmat github.com/RoaringBitmap/roaring
  18. ```
  19. 3. Run go-fuzz:
  20. ```
  21. go-fuzz -bin=./roaring-fuzz.zip -workdir=workdir/ -timeout=200
  22. ```
  23. You should see output like...
  24. ```
  25. 2016/09/16 13:58:35 slaves: 8, corpus: 1 (3s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 3s
  26. 2016/09/16 13:58:38 slaves: 8, corpus: 1 (6s ago), crashers: 0, restarts: 1/0, execs: 0 (0/sec), cover: 0, uptime: 6s
  27. 2016/09/16 13:58:41 slaves: 8, corpus: 1 (9s ago), crashers: 0, restarts: 1/44, execs: 44 (5/sec), cover: 0, uptime: 9s
  28. 2016/09/16 13:58:44 slaves: 8, corpus: 1 (12s ago), crashers: 0, restarts: 1/45, execs: 45 (4/sec), cover: 0, uptime: 12s
  29. 2016/09/16 13:58:47 slaves: 8, corpus: 1 (15s ago), crashers: 0, restarts: 1/46, execs: 46 (3/sec), cover: 0, uptime: 15s
  30. 2016/09/16 13:58:50 slaves: 8, corpus: 1 (18s ago), crashers: 0, restarts: 1/47, execs: 47 (3/sec), cover: 0, uptime: 18s
  31. 2016/09/16 13:58:53 slaves: 8, corpus: 1 (21s ago), crashers: 0, restarts: 1/63, execs: 63 (3/sec), cover: 0, uptime: 21s
  32. 2016/09/16 13:58:56 slaves: 8, corpus: 1 (24s ago), crashers: 0, restarts: 1/65, execs: 65 (3/sec), cover: 0, uptime: 24s
  33. 2016/09/16 13:58:59 slaves: 8, corpus: 1 (27s ago), crashers: 0, restarts: 1/66, execs: 66 (2/sec), cover: 0, uptime: 27s
  34. 2016/09/16 13:59:02 slaves: 8, corpus: 1 (30s ago), crashers: 0, restarts: 1/67, execs: 67 (2/sec), cover: 0, uptime: 30s
  35. 2016/09/16 13:59:05 slaves: 8, corpus: 1 (33s ago), crashers: 0, restarts: 1/83, execs: 83 (3/sec), cover: 0, uptime: 33s
  36. 2016/09/16 13:59:08 slaves: 8, corpus: 1 (36s ago), crashers: 0, restarts: 1/84, execs: 84 (2/sec), cover: 0, uptime: 36s
  37. 2016/09/16 13:59:11 slaves: 8, corpus: 2 (0s ago), crashers: 0, restarts: 1/85, execs: 85 (2/sec), cover: 0, uptime: 39s
  38. 2016/09/16 13:59:14 slaves: 8, corpus: 17 (2s ago), crashers: 0, restarts: 1/86, execs: 86 (2/sec), cover: 480, uptime: 42s
  39. 2016/09/16 13:59:17 slaves: 8, corpus: 17 (5s ago), crashers: 0, restarts: 1/66, execs: 132 (3/sec), cover: 487, uptime: 45s
  40. 2016/09/16 13:59:20 slaves: 8, corpus: 17 (8s ago), crashers: 0, restarts: 1/440, execs: 2645 (55/sec), cover: 487, uptime: 48s
  41. ```
  42. Let it run, and if the # of crashers is > 0, check out the reports in
  43. the workdir where you should be able to find the panic goroutine stack
  44. traces.
  45. */
  46. package roaring
  47. import (
  48. "fmt"
  49. "sort"
  50. "github.com/mschoch/smat"
  51. "github.com/willf/bitset"
  52. )
  53. // fuzz test using state machine driven by byte stream.
  54. func FuzzSmat(data []byte) int {
  55. return smat.Fuzz(&smatContext{}, smat.ActionID('S'), smat.ActionID('T'),
  56. smatActionMap, data)
  57. }
  58. var smatDebug = false
  59. func smatLog(prefix, format string, args ...interface{}) {
  60. if smatDebug {
  61. fmt.Print(prefix)
  62. fmt.Printf(format, args...)
  63. }
  64. }
  65. type smatContext struct {
  66. pairs []*smatPair
  67. // Two registers, x & y.
  68. x int
  69. y int
  70. actions int
  71. }
  72. type smatPair struct {
  73. bm *Bitmap
  74. bs *bitset.BitSet
  75. }
  76. // ------------------------------------------------------------------
  77. var smatActionMap = smat.ActionMap{
  78. smat.ActionID('X'): smatAction("x++", smatWrap(func(c *smatContext) { c.x++ })),
  79. smat.ActionID('x'): smatAction("x--", smatWrap(func(c *smatContext) { c.x-- })),
  80. smat.ActionID('Y'): smatAction("y++", smatWrap(func(c *smatContext) { c.y++ })),
  81. smat.ActionID('y'): smatAction("y--", smatWrap(func(c *smatContext) { c.y-- })),
  82. smat.ActionID('*'): smatAction("x*y", smatWrap(func(c *smatContext) { c.x = c.x * c.y })),
  83. smat.ActionID('<'): smatAction("x<<", smatWrap(func(c *smatContext) { c.x = c.x << 1 })),
  84. smat.ActionID('^'): smatAction("swap", smatWrap(func(c *smatContext) { c.x, c.y = c.y, c.x })),
  85. smat.ActionID('['): smatAction(" pushPair", smatWrap(smatPushPair)),
  86. smat.ActionID(']'): smatAction(" popPair", smatWrap(smatPopPair)),
  87. smat.ActionID('B'): smatAction(" setBit", smatWrap(smatSetBit)),
  88. smat.ActionID('b'): smatAction(" removeBit", smatWrap(smatRemoveBit)),
  89. smat.ActionID('o'): smatAction(" or", smatWrap(smatOr)),
  90. smat.ActionID('a'): smatAction(" and", smatWrap(smatAnd)),
  91. smat.ActionID('#'): smatAction(" cardinality", smatWrap(smatCardinality)),
  92. smat.ActionID('O'): smatAction(" orCardinality", smatWrap(smatOrCardinality)),
  93. smat.ActionID('A'): smatAction(" andCardinality", smatWrap(smatAndCardinality)),
  94. smat.ActionID('c'): smatAction(" clear", smatWrap(smatClear)),
  95. smat.ActionID('r'): smatAction(" runOptimize", smatWrap(smatRunOptimize)),
  96. smat.ActionID('e'): smatAction(" isEmpty", smatWrap(smatIsEmpty)),
  97. smat.ActionID('i'): smatAction(" intersects", smatWrap(smatIntersects)),
  98. smat.ActionID('f'): smatAction(" flip", smatWrap(smatFlip)),
  99. smat.ActionID('-'): smatAction(" difference", smatWrap(smatDifference)),
  100. }
  101. var smatRunningPercentActions []smat.PercentAction
  102. func init() {
  103. var ids []int
  104. for actionId := range smatActionMap {
  105. ids = append(ids, int(actionId))
  106. }
  107. sort.Ints(ids)
  108. pct := 100 / len(smatActionMap)
  109. for _, actionId := range ids {
  110. smatRunningPercentActions = append(smatRunningPercentActions,
  111. smat.PercentAction{pct, smat.ActionID(actionId)})
  112. }
  113. smatActionMap[smat.ActionID('S')] = smatAction("SETUP", smatSetupFunc)
  114. smatActionMap[smat.ActionID('T')] = smatAction("TEARDOWN", smatTeardownFunc)
  115. }
  116. // We only have one smat state: running.
  117. func smatRunning(next byte) smat.ActionID {
  118. return smat.PercentExecute(next, smatRunningPercentActions...)
  119. }
  120. func smatAction(name string, f func(ctx smat.Context) (smat.State, error)) func(smat.Context) (smat.State, error) {
  121. return func(ctx smat.Context) (smat.State, error) {
  122. c := ctx.(*smatContext)
  123. c.actions++
  124. smatLog(" ", "%s\n", name)
  125. return f(ctx)
  126. }
  127. }
  128. // Creates an smat action func based on a simple callback.
  129. func smatWrap(cb func(c *smatContext)) func(smat.Context) (next smat.State, err error) {
  130. return func(ctx smat.Context) (next smat.State, err error) {
  131. c := ctx.(*smatContext)
  132. cb(c)
  133. return smatRunning, nil
  134. }
  135. }
  136. // Invokes a callback function with the input v bounded to len(c.pairs).
  137. func (c *smatContext) withPair(v int, cb func(*smatPair)) {
  138. if len(c.pairs) > 0 {
  139. if v < 0 {
  140. v = -v
  141. }
  142. v = v % len(c.pairs)
  143. cb(c.pairs[v])
  144. }
  145. }
  146. // ------------------------------------------------------------------
  147. func smatSetupFunc(ctx smat.Context) (next smat.State, err error) {
  148. return smatRunning, nil
  149. }
  150. func smatTeardownFunc(ctx smat.Context) (next smat.State, err error) {
  151. return nil, err
  152. }
  153. // ------------------------------------------------------------------
  154. func smatPushPair(c *smatContext) {
  155. c.pairs = append(c.pairs, &smatPair{
  156. bm: NewBitmap(),
  157. bs: bitset.New(100),
  158. })
  159. }
  160. func smatPopPair(c *smatContext) {
  161. if len(c.pairs) > 0 {
  162. c.pairs = c.pairs[0 : len(c.pairs)-1]
  163. }
  164. }
  165. func smatSetBit(c *smatContext) {
  166. c.withPair(c.x, func(p *smatPair) {
  167. y := uint32(c.y)
  168. p.bm.AddInt(int(y))
  169. p.bs.Set(uint(y))
  170. p.checkEquals()
  171. })
  172. }
  173. func smatRemoveBit(c *smatContext) {
  174. c.withPair(c.x, func(p *smatPair) {
  175. y := uint32(c.y)
  176. p.bm.Remove(y)
  177. p.bs.Clear(uint(y))
  178. p.checkEquals()
  179. })
  180. }
  181. func smatAnd(c *smatContext) {
  182. c.withPair(c.x, func(px *smatPair) {
  183. c.withPair(c.y, func(py *smatPair) {
  184. px.bm.And(py.bm)
  185. px.bs = px.bs.Intersection(py.bs)
  186. px.checkEquals()
  187. py.checkEquals()
  188. })
  189. })
  190. }
  191. func smatOr(c *smatContext) {
  192. c.withPair(c.x, func(px *smatPair) {
  193. c.withPair(c.y, func(py *smatPair) {
  194. px.bm.Or(py.bm)
  195. px.bs = px.bs.Union(py.bs)
  196. px.checkEquals()
  197. py.checkEquals()
  198. })
  199. })
  200. }
  201. func smatAndCardinality(c *smatContext) {
  202. c.withPair(c.x, func(px *smatPair) {
  203. c.withPair(c.y, func(py *smatPair) {
  204. c0 := px.bm.AndCardinality(py.bm)
  205. c1 := px.bs.IntersectionCardinality(py.bs)
  206. if c0 != uint64(c1) {
  207. panic("expected same add cardinality")
  208. }
  209. px.checkEquals()
  210. py.checkEquals()
  211. })
  212. })
  213. }
  214. func smatOrCardinality(c *smatContext) {
  215. c.withPair(c.x, func(px *smatPair) {
  216. c.withPair(c.y, func(py *smatPair) {
  217. c0 := px.bm.OrCardinality(py.bm)
  218. c1 := px.bs.UnionCardinality(py.bs)
  219. if c0 != uint64(c1) {
  220. panic("expected same or cardinality")
  221. }
  222. px.checkEquals()
  223. py.checkEquals()
  224. })
  225. })
  226. }
  227. func smatRunOptimize(c *smatContext) {
  228. c.withPair(c.x, func(px *smatPair) {
  229. px.bm.RunOptimize()
  230. px.checkEquals()
  231. })
  232. }
  233. func smatClear(c *smatContext) {
  234. c.withPair(c.x, func(px *smatPair) {
  235. px.bm.Clear()
  236. px.bs = px.bs.ClearAll()
  237. px.checkEquals()
  238. })
  239. }
  240. func smatCardinality(c *smatContext) {
  241. c.withPair(c.x, func(px *smatPair) {
  242. c0 := px.bm.GetCardinality()
  243. c1 := px.bs.Count()
  244. if c0 != uint64(c1) {
  245. panic("expected same cardinality")
  246. }
  247. })
  248. }
  249. func smatIsEmpty(c *smatContext) {
  250. c.withPair(c.x, func(px *smatPair) {
  251. c0 := px.bm.IsEmpty()
  252. c1 := px.bs.None()
  253. if c0 != c1 {
  254. panic("expected same is empty")
  255. }
  256. })
  257. }
  258. func smatIntersects(c *smatContext) {
  259. c.withPair(c.x, func(px *smatPair) {
  260. c.withPair(c.y, func(py *smatPair) {
  261. v0 := px.bm.Intersects(py.bm)
  262. v1 := px.bs.IntersectionCardinality(py.bs) > 0
  263. if v0 != v1 {
  264. panic("intersects not equal")
  265. }
  266. px.checkEquals()
  267. py.checkEquals()
  268. })
  269. })
  270. }
  271. func smatFlip(c *smatContext) {
  272. c.withPair(c.x, func(p *smatPair) {
  273. y := uint32(c.y)
  274. p.bm.Flip(uint64(y), uint64(y)+1)
  275. p.bs = p.bs.Flip(uint(y))
  276. p.checkEquals()
  277. })
  278. }
  279. func smatDifference(c *smatContext) {
  280. c.withPair(c.x, func(px *smatPair) {
  281. c.withPair(c.y, func(py *smatPair) {
  282. px.bm.AndNot(py.bm)
  283. px.bs = px.bs.Difference(py.bs)
  284. px.checkEquals()
  285. py.checkEquals()
  286. })
  287. })
  288. }
  289. func (p *smatPair) checkEquals() {
  290. if !p.equalsBitSet(p.bs, p.bm) {
  291. panic("bitset mismatch")
  292. }
  293. }
  294. func (p *smatPair) equalsBitSet(a *bitset.BitSet, b *Bitmap) bool {
  295. for i, e := a.NextSet(0); e; i, e = a.NextSet(i + 1) {
  296. if !b.ContainsInt(int(i)) {
  297. fmt.Printf("in a bitset, not b bitmap, i: %d\n", i)
  298. fmt.Printf(" a bitset: %s\n b bitmap: %s\n",
  299. a.String(), b.String())
  300. return false
  301. }
  302. }
  303. i := b.Iterator()
  304. for i.HasNext() {
  305. v := i.Next()
  306. if !a.Test(uint(v)) {
  307. fmt.Printf("in b bitmap, not a bitset, v: %d\n", v)
  308. fmt.Printf(" a bitset: %s\n b bitmap: %s\n",
  309. a.String(), b.String())
  310. return false
  311. }
  312. }
  313. return true
  314. }