out_message_test.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. package buffer
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "fmt"
  6. "io"
  7. "reflect"
  8. "testing"
  9. "unsafe"
  10. "github.com/jacobsa/fuse/internal/fusekernel"
  11. "github.com/kylelemons/godebug/pretty"
  12. )
  13. func toByteSlice(p unsafe.Pointer, n int) []byte {
  14. sh := reflect.SliceHeader{
  15. Data: uintptr(p),
  16. Len: n,
  17. Cap: n,
  18. }
  19. return *(*[]byte)(unsafe.Pointer(&sh))
  20. }
  21. // fillWithGarbage writes random data to [p, p+n).
  22. func fillWithGarbage(p unsafe.Pointer, n int) (err error) {
  23. b := toByteSlice(p, n)
  24. _, err = io.ReadFull(rand.Reader, b)
  25. return
  26. }
  27. func randBytes(n int) (b []byte, err error) {
  28. b = make([]byte, n)
  29. _, err = io.ReadFull(rand.Reader, b)
  30. return
  31. }
  32. // findNonZero finds the offset of the first non-zero byte in [p, p+n). If
  33. // none, it returns n.
  34. func findNonZero(p unsafe.Pointer, n int) int {
  35. b := toByteSlice(p, n)
  36. for i, x := range b {
  37. if x != 0 {
  38. return i
  39. }
  40. }
  41. return n
  42. }
  43. func TestMemclr(t *testing.T) {
  44. // All sizes up to 32 bytes.
  45. var sizes []int
  46. for i := 0; i <= 32; i++ {
  47. sizes = append(sizes, i)
  48. }
  49. // And a few hand-chosen sizes.
  50. sizes = append(sizes, []int{
  51. 39, 41, 64, 127, 128, 129,
  52. 1<<20 - 1,
  53. 1 << 20,
  54. 1<<20 + 1,
  55. }...)
  56. // For each size, fill a buffer with random bytes and then zero it.
  57. for _, size := range sizes {
  58. size := size
  59. t.Run(fmt.Sprintf("size=%d", size), func(t *testing.T) {
  60. // Generate
  61. b, err := randBytes(size)
  62. if err != nil {
  63. t.Fatalf("randBytes: %v", err)
  64. }
  65. // Clear
  66. var p unsafe.Pointer
  67. if len(b) != 0 {
  68. p = unsafe.Pointer(&b[0])
  69. }
  70. memclr(p, uintptr(len(b)))
  71. // Check
  72. if i := findNonZero(p, len(b)); i != len(b) {
  73. t.Fatalf("non-zero byte at offset %d", i)
  74. }
  75. })
  76. }
  77. }
  78. func TestOutMessageAppend(t *testing.T) {
  79. var om OutMessage
  80. om.Reset()
  81. // Append some payload.
  82. const wantPayloadStr = "tacoburrito"
  83. wantPayload := []byte(wantPayloadStr)
  84. om.Append(wantPayload[:4])
  85. om.Append(wantPayload[4:])
  86. // The result should be a zeroed header followed by the desired payload.
  87. const wantLen = OutMessageHeaderSize + len(wantPayloadStr)
  88. if got, want := om.Len(), wantLen; got != want {
  89. t.Errorf("om.Len() = %d, want %d", got, want)
  90. }
  91. b := om.Bytes()
  92. if got, want := len(b), wantLen; got != want {
  93. t.Fatalf("len(om.Bytes()) = %d, want %d", got, want)
  94. }
  95. want := append(
  96. make([]byte, OutMessageHeaderSize),
  97. wantPayload...)
  98. if !bytes.Equal(b, want) {
  99. t.Error("messages differ")
  100. }
  101. }
  102. func TestOutMessageAppendString(t *testing.T) {
  103. var om OutMessage
  104. om.Reset()
  105. // Append some payload.
  106. const wantPayload = "tacoburrito"
  107. om.AppendString(wantPayload[:4])
  108. om.AppendString(wantPayload[4:])
  109. // The result should be a zeroed header followed by the desired payload.
  110. const wantLen = OutMessageHeaderSize + len(wantPayload)
  111. if got, want := om.Len(), wantLen; got != want {
  112. t.Errorf("om.Len() = %d, want %d", got, want)
  113. }
  114. b := om.Bytes()
  115. if got, want := len(b), wantLen; got != want {
  116. t.Fatalf("len(om.Bytes()) = %d, want %d", got, want)
  117. }
  118. want := append(
  119. make([]byte, OutMessageHeaderSize),
  120. wantPayload...)
  121. if !bytes.Equal(b, want) {
  122. t.Error("messages differ")
  123. }
  124. }
  125. func TestOutMessageShrinkTo(t *testing.T) {
  126. // Set up a buffer with some payload.
  127. var om OutMessage
  128. om.Reset()
  129. om.AppendString("taco")
  130. om.AppendString("burrito")
  131. // Shrink it.
  132. om.ShrinkTo(OutMessageHeaderSize + len("taco"))
  133. // The result should be a zeroed header followed by "taco".
  134. const wantLen = OutMessageHeaderSize + len("taco")
  135. if got, want := om.Len(), wantLen; got != want {
  136. t.Errorf("om.Len() = %d, want %d", got, want)
  137. }
  138. b := om.Bytes()
  139. if got, want := len(b), wantLen; got != want {
  140. t.Fatalf("len(om.Bytes()) = %d, want %d", got, want)
  141. }
  142. want := append(
  143. make([]byte, OutMessageHeaderSize),
  144. "taco"...)
  145. if !bytes.Equal(b, want) {
  146. t.Error("messages differ")
  147. }
  148. }
  149. func TestOutMessageHeader(t *testing.T) {
  150. var om OutMessage
  151. om.Reset()
  152. // Fill in the header.
  153. want := fusekernel.OutHeader{
  154. Len: 0xdeadbeef,
  155. Error: -31231917,
  156. Unique: 0xcafebabeba5eba11,
  157. }
  158. h := om.OutHeader()
  159. if h == nil {
  160. t.Fatal("OutHeader returned nil")
  161. }
  162. *h = want
  163. // Check that the result is as expected.
  164. b := om.Bytes()
  165. if len(b) != int(unsafe.Sizeof(want)) {
  166. t.Fatalf("unexpected length %d; want %d", len(b), unsafe.Sizeof(want))
  167. }
  168. got := *(*fusekernel.OutHeader)(unsafe.Pointer(&b[0]))
  169. if diff := pretty.Compare(got, want); diff != "" {
  170. t.Errorf("diff -got +want:\n%s", diff)
  171. }
  172. }
  173. func TestOutMessageReset(t *testing.T) {
  174. var om OutMessage
  175. h := om.OutHeader()
  176. const trials = 10
  177. for i := 0; i < trials; i++ {
  178. // Fill the header with garbage.
  179. err := fillWithGarbage(unsafe.Pointer(h), int(unsafe.Sizeof(*h)))
  180. if err != nil {
  181. t.Fatalf("fillWithGarbage: %v", err)
  182. }
  183. // Ensure a non-zero payload length.
  184. if p := om.GrowNoZero(128); p == nil {
  185. t.Fatal("GrowNoZero failed")
  186. }
  187. // Reset.
  188. om.Reset()
  189. // Check that the length was updated.
  190. if got, want := om.Len(), OutMessageHeaderSize; got != want {
  191. t.Fatalf("om.Len() = %d, want %d", got, want)
  192. }
  193. // Check that the header was zeroed.
  194. if h.Len != 0 {
  195. t.Fatalf("non-zero Len %v", h.Len)
  196. }
  197. if h.Error != 0 {
  198. t.Fatalf("non-zero Error %v", h.Error)
  199. }
  200. if h.Unique != 0 {
  201. t.Fatalf("non-zero Unique %v", h.Unique)
  202. }
  203. }
  204. }
  205. func TestOutMessageGrow(t *testing.T) {
  206. var om OutMessage
  207. om.Reset()
  208. // Set up garbage where the payload will soon be.
  209. const payloadSize = 1234
  210. {
  211. p := om.GrowNoZero(payloadSize)
  212. if p == nil {
  213. t.Fatal("GrowNoZero failed")
  214. }
  215. err := fillWithGarbage(p, payloadSize)
  216. if err != nil {
  217. t.Fatalf("fillWithGarbage: %v", err)
  218. }
  219. om.ShrinkTo(OutMessageHeaderSize)
  220. }
  221. // Call Grow.
  222. if p := om.Grow(payloadSize); p == nil {
  223. t.Fatal("Grow failed")
  224. }
  225. // Check the resulting length in two ways.
  226. const wantLen = payloadSize + OutMessageHeaderSize
  227. if got, want := om.Len(), wantLen; got != want {
  228. t.Errorf("om.Len() = %d, want %d", got)
  229. }
  230. b := om.Bytes()
  231. if got, want := len(b), wantLen; got != want {
  232. t.Fatalf("len(om.Len()) = %d, want %d", got)
  233. }
  234. // Check that the payload was zeroed.
  235. for i, x := range b[OutMessageHeaderSize:] {
  236. if x != 0 {
  237. t.Fatalf("non-zero byte 0x%02x at payload offset %d", x, i)
  238. }
  239. }
  240. }
  241. func BenchmarkOutMessageReset(b *testing.B) {
  242. // A single buffer, which should fit in some level of CPU cache.
  243. b.Run("Single buffer", func(b *testing.B) {
  244. var om OutMessage
  245. for i := 0; i < b.N; i++ {
  246. om.Reset()
  247. }
  248. b.SetBytes(int64(unsafe.Offsetof(om.payload)))
  249. })
  250. // Many megabytes worth of buffers, which should defeat the CPU cache.
  251. b.Run("Many buffers", func(b *testing.B) {
  252. // The number of messages; intentionally a power of two.
  253. const numMessages = 128
  254. var oms [numMessages]OutMessage
  255. if s := unsafe.Sizeof(oms); s < 128<<20 {
  256. panic(fmt.Sprintf("Array is too small; total size: %d", s))
  257. }
  258. for i := 0; i < b.N; i++ {
  259. oms[i%numMessages].Reset()
  260. }
  261. b.SetBytes(int64(unsafe.Offsetof(oms[0].payload)))
  262. })
  263. }
  264. func BenchmarkOutMessageGrowShrink(b *testing.B) {
  265. // A single buffer, which should fit in some level of CPU cache.
  266. b.Run("Single buffer", func(b *testing.B) {
  267. var om OutMessage
  268. for i := 0; i < b.N; i++ {
  269. om.Grow(MaxReadSize)
  270. om.ShrinkTo(OutMessageHeaderSize)
  271. }
  272. b.SetBytes(int64(MaxReadSize))
  273. })
  274. // Many megabytes worth of buffers, which should defeat the CPU cache.
  275. b.Run("Many buffers", func(b *testing.B) {
  276. // The number of messages; intentionally a power of two.
  277. const numMessages = 128
  278. var oms [numMessages]OutMessage
  279. if s := unsafe.Sizeof(oms); s < 128<<20 {
  280. panic(fmt.Sprintf("Array is too small; total size: %d", s))
  281. }
  282. for i := 0; i < b.N; i++ {
  283. oms[i%numMessages].Grow(MaxReadSize)
  284. oms[i%numMessages].ShrinkTo(OutMessageHeaderSize)
  285. }
  286. b.SetBytes(int64(MaxReadSize))
  287. })
  288. }