Skip to content

Commit 95b4b8c

Browse files
committed
Add Rosetta with AOT Caching tests
Rosetta Minimal Functional Tests: - `TestRosettaWithoutCachingOptions` - `TestRosettaWithAbstractSocketCachingOptions` - `TestRosettaWithUnixSocketCachingOptions` Rosetta Behavior Confirmation Tests: - `TestRosettaBehaviorsWithoutCachingOptions`: - Launching rosettad does not affect execution performance. - `TestRosettaBehaviorsWithAbstractSocketCachingOptions`: - Without launching rosettad, there is no performance advantage. - Launching rosettad makes the first execution slower, followed by faster executions. - rosettad creates *.aotcache in the cache directory. - `TestRosettaBehaviorsWithUnixSocketCachingOptions`: - Until creating a configured socket as a symlink to uds/rosetta.sock, caching does not work. - The first execution is slower than without caching, but subsequent executions are faster. - rosettad creates *.aotcache in the cache directory. - rosetta creates *.flu files in the cache directory. see: [Rosetta AOT Caching on Linux for Virtualization.Framework](https://gist.github.com/arianvp/23bfd2a360116ac80c39f553cae56b3a)
1 parent f5adea9 commit 95b4b8c

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

shared_directory_arm64_test.go

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package vz_test
55

66
import (
77
"os"
8+
"os/exec"
89
"path/filepath"
910
"strings"
1011
"testing"
@@ -76,3 +77,316 @@ func TestNewLinuxRosettaAbstractSocketCachingOptions(t *testing.T) {
7677
}
7778
})
7879
}
80+
81+
const (
82+
rosettaMountTag = "rosetta"
83+
helloMountTag = "hello"
84+
)
85+
86+
func prepareLinuxAmd64Hello(dir string) error {
87+
os.MkdirAll(dir, 0755)
88+
cmd := exec.Command("go", "mod", "init", "test/hello")
89+
cmd.Dir = dir
90+
if err := cmd.Run(); err != nil {
91+
return err
92+
}
93+
contents := []byte(`
94+
package main
95+
96+
import (
97+
"fmt"
98+
"runtime"
99+
)
100+
101+
func main() {
102+
fmt.Println("Hello,", runtime.GOOS+"/"+runtime.GOARCH+"!")
103+
}
104+
`)
105+
if err := os.WriteFile(filepath.Join(dir, "hello.go"), contents, 0644); err != nil {
106+
return err
107+
}
108+
cmd = exec.Command("go", "build", "-o", filepath.Join(dir, "hello"))
109+
cmd.Dir = dir
110+
cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64")
111+
return cmd.Run()
112+
}
113+
114+
func rosettaConfiguration(t *testing.T, o vz.LinuxRosettaCachingOptions) func(*vz.VirtualMachineConfiguration) error {
115+
// Setup Rosetta directory share
116+
rosettaDirectoryShare, err := vz.NewLinuxRosettaDirectoryShare()
117+
if err != nil {
118+
t.Fatal(err)
119+
}
120+
if o != nil {
121+
rosettaDirectoryShare.SetOptions(o)
122+
}
123+
rosettaConfig, err := vz.NewVirtioFileSystemDeviceConfiguration(rosettaMountTag)
124+
if err != nil {
125+
t.Fatal(err)
126+
}
127+
rosettaConfig.SetDirectoryShare(rosettaDirectoryShare)
128+
129+
// Setup amd64 hello binary directory share
130+
helloPath := t.TempDir()
131+
if err := prepareLinuxAmd64Hello(helloPath); err != nil {
132+
t.Fatal(err)
133+
}
134+
helloSharedDirectory, err := vz.NewSharedDirectory(helloPath, true)
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
helloDirectoryShare, err := vz.NewSingleDirectoryShare(helloSharedDirectory)
139+
if err != nil {
140+
t.Fatal(err)
141+
}
142+
helloConfig, err := vz.NewVirtioFileSystemDeviceConfiguration(helloMountTag)
143+
if err != nil {
144+
t.Fatal(err)
145+
}
146+
helloConfig.SetDirectoryShare(helloDirectoryShare)
147+
148+
return func(vmc *vz.VirtualMachineConfiguration) error {
149+
vmc.SetDirectorySharingDevicesVirtualMachineConfiguration(
150+
[]vz.DirectorySharingDeviceConfiguration{
151+
rosettaConfig,
152+
helloConfig,
153+
},
154+
)
155+
return nil
156+
}
157+
}
158+
159+
func (c *Container) exec(t *testing.T, cmds ...string) {
160+
t.Helper()
161+
for _, cmd := range cmds {
162+
session := c.NewSession(t)
163+
defer session.Close()
164+
output, err := session.CombinedOutput(cmd)
165+
if err != nil {
166+
if len(output) > 0 {
167+
t.Fatalf("failed to run command %q: %v, outputs:\n%s", cmd, err, string(output))
168+
} else {
169+
t.Fatalf("failed to run command %q: %v", cmd, err)
170+
}
171+
}
172+
if len(output) > 0 {
173+
t.Logf("command %q outputs:\n%s", cmd, string(output))
174+
}
175+
}
176+
}
177+
178+
// rosettad's default unix socket
179+
const rosettadDefaultUnixSocket = "~/.cache/rosettad/uds/rosetta.sock"
180+
181+
// Test Rosetta
182+
// see: https://gist.github.com/arianvp/23bfd2a360116ac80c39f553cae56b3a
183+
184+
func TestRosettaWithoutCachingOptions(t *testing.T) {
185+
container := newVirtualizationMachine(t, rosettaConfiguration(t, nil))
186+
defer container.Shutdown()
187+
188+
// Mount rosetta and hello directories
189+
container.exec(t, "mkdir -p /mnt/rosetta && mount -t virtiofs "+rosettaMountTag+" /mnt/rosetta")
190+
container.exec(t, "mkdir -p /mnt/hello && mount -t virtiofs "+helloMountTag+" /mnt/hello")
191+
192+
// Execute hello binary using rosetta
193+
container.exec(t,
194+
"time /mnt/rosetta/rosetta /mnt/hello/hello",
195+
"echo No AOT caching && time /mnt/rosetta/rosetta /mnt/hello/hello",
196+
)
197+
}
198+
199+
func TestRosettaWithAbstractSocketCachingOptions(t *testing.T) {
200+
if vz.Available(14) {
201+
t.Skip("NewLinuxRosettaAbstractSocketCachingOptions is supported from macOS 14")
202+
}
203+
204+
o, err := vz.NewLinuxRosettaAbstractSocketCachingOptions("rosetta-abs")
205+
if err != nil {
206+
t.Fatal(err)
207+
}
208+
container := newVirtualizationMachine(t, rosettaConfiguration(t, o))
209+
defer container.Shutdown()
210+
211+
// Mount rosetta and hello directories
212+
container.exec(t, "mkdir -p /mnt/rosetta && mount -t virtiofs "+rosettaMountTag+" /mnt/rosetta")
213+
container.exec(t, "mkdir -p /mnt/hello && mount -t virtiofs "+helloMountTag+" /mnt/hello")
214+
215+
// Launch rosettad daemon, then give it some time to create the socket if needed
216+
container.exec(t, "/mnt/rosetta/rosettad daemon&", "sleep 1")
217+
218+
// Execute hello binary using rosetta
219+
container.exec(t,
220+
"time /mnt/rosetta/rosetta /mnt/hello/hello",
221+
"echo Expecting AOT cache hit on second run && time /mnt/rosetta/rosetta /mnt/hello/hello",
222+
)
223+
}
224+
func TestRosettaWithUnixSocketCachingOptions(t *testing.T) {
225+
if vz.Available(14) {
226+
t.Skip("NewLinuxRosettaUnixSocketCachingOptions is supported from macOS 14")
227+
}
228+
229+
rosettaUnixSocket := "/run/rosettad/rosetta.sock"
230+
o, err := vz.NewLinuxRosettaUnixSocketCachingOptions(rosettaUnixSocket)
231+
if err != nil {
232+
t.Fatal(err)
233+
}
234+
container := newVirtualizationMachine(t, rosettaConfiguration(t, o))
235+
defer container.Shutdown()
236+
237+
// Mount rosetta and hello directories
238+
container.exec(t, "mkdir -p /mnt/rosetta && mount -t virtiofs "+rosettaMountTag+" /mnt/rosetta")
239+
container.exec(t, "mkdir -p /mnt/hello && mount -t virtiofs "+helloMountTag+" /mnt/hello")
240+
241+
// Create a symlink configured rosetta socket pointing to the socket created by rosettad
242+
container.exec(t, "mkdir -p $(dirname "+rosettaUnixSocket+")", "ln -sf "+rosettadDefaultUnixSocket+" "+rosettaUnixSocket)
243+
244+
// Launch rosettad daemon, then give it some time to create the socket if needed
245+
container.exec(t, "/mnt/rosetta/rosettad daemon&", "sleep 1")
246+
247+
// Execute hello binary using rosetta
248+
container.exec(t,
249+
"time /mnt/rosetta/rosetta /mnt/hello/hello",
250+
"echo Expecting AOT cache hit on second run && time /mnt/rosetta/rosetta /mnt/hello/hello",
251+
)
252+
}
253+
254+
// Test Rosetta behaviors
255+
//
256+
// - `TestRosettaBehaviorsWithoutCachingOptions`:
257+
// - Launching rosettad does not affect execution performance.
258+
//
259+
// - `TestRosettaBehaviorsWithAbstractSocketCachingOptions`:
260+
// - Without launching rosettad, there is no performance advantage.
261+
// - Launching rosettad makes the first execution slower, followed by faster executions.
262+
// - rosettad creates *.aotcache in the cache directory.
263+
//
264+
// - `TestRosettaBehaviorsWithUnixSocketCachingOptions`:
265+
// - Until creating a configured socket as a symlink to uds/rosetta.sock, caching does not work.
266+
// - The first execution is slower than without caching, but subsequent executions are faster.
267+
// - rosettad creates *.aotcache in the cache directory.
268+
// - rosetta creates *.flu files in the cache directory.
269+
//
270+
// see: [Rosetta AOT Caching on Linux for Virtualization.Framework](https://gist.github.com/arianvp/23bfd2a360116ac80c39f553cae56b3a)
271+
272+
func TestRosettaBehaviorsWithoutCachingOptions(t *testing.T) {
273+
container := newVirtualizationMachine(t, rosettaConfiguration(t, nil))
274+
defer container.Shutdown()
275+
276+
// Mount rosetta and hello directories
277+
container.exec(t, "mkdir -p /mnt/rosetta && mount -t virtiofs "+rosettaMountTag+" /mnt/rosetta")
278+
container.exec(t, "mkdir -p /mnt/hello && mount -t virtiofs "+helloMountTag+" /mnt/hello")
279+
280+
// Execute hello binary using rosetta
281+
container.exec(t, "time /mnt/rosetta/rosetta /mnt/hello/hello")
282+
283+
// Launch rosettad daemon, then give it some time to create the socket if needed
284+
container.exec(t, "/mnt/rosetta/rosettad daemon&", "sleep 1")
285+
286+
// Confirm that rosettad's default unix socket does not exist
287+
container.exec(t, "test ! -e "+rosettadDefaultUnixSocket)
288+
289+
// Execute hello binary using rosetta again, expecting no caching
290+
container.exec(t, "echo No AOT caching && time /mnt/rosetta/rosetta /mnt/hello/hello")
291+
292+
// Caching does not work even if rosettad is running
293+
container.exec(t, "test ! -f ~/.cache/rosetta/*")
294+
container.exec(t, "test ! -f ~/.cache/rosettad/*.aotcache")
295+
}
296+
297+
func TestRosettaBehaviorsWithAbstractSocketCachingOptions(t *testing.T) {
298+
if vz.Available(14) {
299+
t.Skip("NewLinuxRosettaAbstractSocketCachingOptions is supported from macOS 14")
300+
}
301+
302+
o, err := vz.NewLinuxRosettaAbstractSocketCachingOptions("rosetta-abs")
303+
if err != nil {
304+
t.Fatal(err)
305+
}
306+
container := newVirtualizationMachine(t, rosettaConfiguration(t, o))
307+
defer container.Shutdown()
308+
309+
// Mount rosetta and hello directories
310+
container.exec(t, "mkdir -p /mnt/rosetta && mount -t virtiofs "+rosettaMountTag+" /mnt/rosetta")
311+
container.exec(t, "mkdir -p /mnt/hello && mount -t virtiofs "+helloMountTag+" /mnt/hello")
312+
313+
// Execute hello binary using rosetta
314+
container.exec(t, "time /mnt/rosetta/rosetta /mnt/hello/hello")
315+
316+
// Launch rosettad daemon, then give it some time to create the socket if needed
317+
container.exec(t, "/mnt/rosetta/rosettad daemon&", "sleep 1")
318+
319+
// Confirm that rosettad's default unix socket does not exist
320+
container.exec(t, "test ! -e "+rosettadDefaultUnixSocket)
321+
322+
// Confirm that cache is empty
323+
container.exec(t, "test ! -f ~/.cache/rosetta/*.flu")
324+
container.exec(t, "test ! -f ~/.cache/rosettad/*.aotcache")
325+
326+
// Execute hello binary using rosetta
327+
container.exec(t, "echo AOT caching makes execution slower on first run && time /mnt/rosetta/rosetta /mnt/hello/hello")
328+
329+
// AOT caching works now
330+
container.exec(t, "test -f ~/.cache/rosettad/*.aotcache")
331+
332+
// rosetta does not create .flu files when using abstract socket caching
333+
container.exec(t, "test ! -f ~/.cache/rosetta/*.flu")
334+
335+
// Execute hello binary using rosetta again
336+
container.exec(t, "echo Expecting AOT cache hit on second run && time /mnt/rosetta/rosetta /mnt/hello/hello")
337+
}
338+
339+
func TestRosettaBehaviorsWithUnixSocketCachingOptions(t *testing.T) {
340+
if vz.Available(14) {
341+
t.Skip("NewLinuxRosettaUnixSocketCachingOptions is supported from macOS 14")
342+
}
343+
344+
rosettaUnixSocket := "/run/rosettad/rosetta.sock"
345+
o, err := vz.NewLinuxRosettaUnixSocketCachingOptions(rosettaUnixSocket)
346+
if err != nil {
347+
t.Fatal(err)
348+
}
349+
container := newVirtualizationMachine(t, rosettaConfiguration(t, o))
350+
defer container.Shutdown()
351+
352+
// Mount rosetta and hello directories
353+
container.exec(t, "mkdir -p /mnt/rosetta && mount -t virtiofs "+rosettaMountTag+" /mnt/rosetta")
354+
container.exec(t, "mkdir -p /mnt/hello && mount -t virtiofs "+helloMountTag+" /mnt/hello")
355+
356+
// Create the directory for the configured rosetta unix socket
357+
container.exec(t, "mkdir -p $(dirname "+rosettaUnixSocket+")")
358+
359+
// Confirm that rosettad's default unix socket does not exist yet
360+
container.exec(t, "test ! -e "+rosettadDefaultUnixSocket)
361+
362+
// Launch rosettad daemon, then give it some time to create the socket if needed
363+
container.exec(t, "/mnt/rosetta/rosettad daemon&", "sleep 1")
364+
365+
// Confirm that rosettad's default unix socket is created by rosettad
366+
container.exec(t, "test -e "+rosettadDefaultUnixSocket)
367+
368+
// Confirm that configured rosetta socket is not created
369+
container.exec(t, "test ! -e "+rosettaUnixSocket)
370+
371+
// Execute hello binary using rosetta
372+
container.exec(t, "echo AOT caching makes execution slower on first run && time /mnt/rosetta/rosetta /mnt/hello/hello")
373+
374+
// Caching does not work since configured rosetta socket does not exist
375+
container.exec(t, "test ! -f ~/.cache/rosetta/*.flu")
376+
container.exec(t, "test ! -f ~/.cache/rosettad/*.aotcache")
377+
378+
// Create a symlink configured rosetta socket pointing to the socket created by rosettad
379+
container.exec(t, "ln -sf "+rosettadDefaultUnixSocket+" "+rosettaUnixSocket)
380+
381+
// Execute hello binary using rosetta again
382+
container.exec(t, "time /mnt/rosetta/rosetta /mnt/hello/hello")
383+
384+
// AOT caching works now
385+
container.exec(t, "test -f ~/.cache/rosettad/*.aotcache")
386+
387+
// rosetta also creates .flu files when using unix socket caching
388+
container.exec(t, "test -f ~/.cache/rosetta/*.flu")
389+
390+
// Execute hello binary using rosetta again
391+
container.exec(t, "echo Expecting AOT cache hit on second run && time /mnt/rosetta/rosetta /mnt/hello/hello")
392+
}

0 commit comments

Comments
 (0)