From: Richard W.M. Jones Date: Tue, 5 Oct 2010 21:19:15 +0000 (+0100) Subject: contrib: Visualizing block device access and alignment. X-Git-Tag: 1.5.22~15 X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=3e1a5352703a42980d9855f29fbfa6d5788c56ea;p=libguestfs.git contrib: Visualizing block device access and alignment. --- diff --git a/contrib/visualize-alignment/.gitignore b/contrib/visualize-alignment/.gitignore new file mode 100644 index 0000000..68a2009 --- /dev/null +++ b/contrib/visualize-alignment/.gitignore @@ -0,0 +1,2 @@ +*.eps +*.png diff --git a/contrib/visualize-alignment/README b/contrib/visualize-alignment/README new file mode 100644 index 0000000..917bd77 --- /dev/null +++ b/contrib/visualize-alignment/README @@ -0,0 +1,19 @@ +- guestfish-N-fs-10M.qtr + + The command 'guestfish -N fs:ext2:10' before we modified the + part-disk API to align the partition to 64 sectors. + +- guestfish-N-fs-10M-aligned-part-disk.qtr + + The command 'guestfish -N fs:ext2:10' after we modified the + part-disk API to align the partition to 64 sectors. + +- guestfish-add-mount.qtr + + $ guestfish -a test1.img -m /dev/sda1 + where test1.img was created by the previous command. + +- guestfish-write-hello.qtr + + $ guestfish -a test1.img -m /dev/sda1 write /hello "hello, world." + Note that the trace includes the adding and mounting operations. diff --git a/contrib/visualize-alignment/guestfish-N-fs-10M-aligned-part-disk.qtr b/contrib/visualize-alignment/guestfish-N-fs-10M-aligned-part-disk.qtr new file mode 100644 index 0000000..d99c24d --- /dev/null +++ b/contrib/visualize-alignment/guestfish-N-fs-10M-aligned-part-disk.qtr @@ -0,0 +1,122 @@ +S 20480 +R 0 1 +R 0 8 +R 8 8 +R 20352 8 +R 20464 8 +R 0 8 +R 8 8 +R 20472 8 +R 20216 8 +R 20416 8 +R 20224 8 +R 20080 8 +R 2048 8 +R 24 8 +R 56 8 +R 120 8 +R 16 8 +R 128 8 +R 64 8 +R 512 8 +R 32 8 +R 4096 8 +R 0 32 +R 20352 8 +R 20464 8 +R 0 8 +R 8 8 +R 0 8 +R 0 8 +R 0 32 +R 32 64 +R 128 8 +R 20456 8 +W 0 24 +W 20456 24 +R 0 8 +R 8 24 +W 0 8 +R 20352 8 +R 20464 8 +R 0 32 +R 32 64 +W 0 8 +R 20472 8 +R 20216 8 +R 20472 8 +R 20416 8 +R 20224 8 +R 20080 8 +R 20464 8 +R 8 8 +R 0 8 +R 2048 8 +R 24 8 +R 56 8 +R 120 8 +R 16 8 +R 128 8 +R 64 8 +R 512 8 +R 32 8 +R 4096 8 +R 20288 8 +R 20400 8 +R 64 8 +R 72 8 +R 20416 1 +R 20160 8 +R 20408 8 +R 2112 8 +R 88 8 +R 120 8 +R 184 8 +R 80 8 +R 192 8 +R 128 8 +R 576 8 +R 96 8 +R 4160 8 +R 0 8 +R 64 2 +W 64 379 +W 443 55 +W 16450 4 +W 16532 92 +R 20416 1 +R 20160 1 +R 20161 1 +R 20162 1 +R 20163 5 +R 2112 1 +R 2113 1 +R 2114 6 +R 576 1 +R 577 1 +R 578 6 +R 4160 1 +R 4161 1 +R 4162 6 +R 0 8 +W 16624 230 +W 20288 128 +W 66 2 +R 20288 8 +R 20400 8 +R 64 8 +R 72 8 +R 20416 1 +R 20160 8 +R 20408 8 +R 2112 8 +R 88 8 +R 120 8 +R 184 8 +R 80 8 +R 192 8 +R 128 8 +R 576 8 +R 96 8 +R 4160 8 +R 0 8 diff --git a/contrib/visualize-alignment/guestfish-N-fs-10M.qtr b/contrib/visualize-alignment/guestfish-N-fs-10M.qtr new file mode 100644 index 0000000..4b44e4b --- /dev/null +++ b/contrib/visualize-alignment/guestfish-N-fs-10M.qtr @@ -0,0 +1,122 @@ +S 20480 +R 0 1 +R 0 8 +R 8 8 +R 20352 8 +R 20464 8 +R 0 8 +R 8 8 +R 20472 8 +R 20216 8 +R 20416 8 +R 20224 8 +R 20080 8 +R 2048 8 +R 24 8 +R 56 8 +R 120 8 +R 16 8 +R 128 8 +R 64 8 +R 512 8 +R 32 8 +R 4096 8 +R 0 32 +R 20352 8 +R 20464 8 +R 0 8 +R 8 8 +R 0 8 +R 0 8 +R 0 32 +R 32 64 +R 128 8 +R 20456 8 +W 0 24 +W 20456 24 +R 0 8 +R 8 24 +W 0 8 +R 20352 8 +R 20464 8 +R 0 32 +R 32 64 +W 0 8 +R 20472 8 +R 20216 8 +R 20416 8 +R 20224 8 +R 20080 8 +R 20464 8 +R 8 8 +R 0 8 +R 2048 8 +R 24 8 +R 56 8 +R 120 8 +R 16 8 +R 128 8 +R 64 8 +R 512 8 +R 32 8 +R 4096 8 +R 20225 8 +R 20457 8 +R 1 8 +R 9 8 +R 20473 7 +R 20217 8 +R 20465 8 +R 2049 8 +R 25 8 +R 57 8 +R 121 8 +R 17 8 +R 129 8 +R 65 8 +R 513 8 +R 33 8 +R 4097 8 +R 0 8 +R 1 2 +R 20225 8 +W 1 436 +W 16387 1 +W 16388 3 +W 16469 324 +W 20225 248 +R 20473 7 +R 20217 8 +R 2049 1 +R 2050 1 +R 2051 6 +R 513 1 +R 514 1 +R 515 1 +R 516 1 +R 517 1 +R 518 1 +R 519 1 +R 520 1 +R 4097 1 +R 4098 7 +R 0 8 +W 3 2 +R 20225 8 +R 20457 8 +R 1 8 +R 9 8 +R 20473 7 +R 20217 8 +R 20465 8 +R 2049 8 +R 25 8 +R 57 8 +R 121 8 +R 17 8 +R 129 8 +R 65 8 +R 513 8 +R 33 8 +R 4097 8 +R 0 8 diff --git a/contrib/visualize-alignment/guestfish-add-mount.qtr b/contrib/visualize-alignment/guestfish-add-mount.qtr new file mode 100644 index 0000000..2c99524 --- /dev/null +++ b/contrib/visualize-alignment/guestfish-add-mount.qtr @@ -0,0 +1,72 @@ +S 20480 +R 0 1 +R 0 8 +R 20352 8 +R 20464 8 +R 0 8 +R 8 8 +R 20472 8 +R 20216 8 +R 20416 8 +R 20224 8 +R 20080 8 +R 2048 8 +R 24 8 +R 56 8 +R 120 8 +R 16 8 +R 128 8 +R 64 8 +R 512 8 +R 32 8 +R 4096 8 +R 20288 8 +R 20400 8 +R 64 8 +R 72 8 +R 20416 1 +R 20160 8 +R 20408 8 +R 2112 8 +R 88 8 +R 120 8 +R 184 8 +R 80 8 +R 192 8 +R 128 8 +R 576 8 +R 96 8 +R 4160 8 +R 0 8 +R 0 32 +R 20288 1 +R 20400 1 +R 64 1 +R 72 1 +R 64 4 +R 64 4 +R 20288 8 +R 20400 8 +R 64 8 +R 72 8 +R 20416 1 +R 20160 8 +R 20408 8 +R 2112 8 +R 88 8 +R 120 8 +R 184 8 +R 80 8 +R 192 8 +R 128 8 +R 576 8 +R 96 8 +R 4160 8 +R 66 2 +R 68 2 +R 152 2 +W 66 2 +W 66 2 +W 66 2 +W 66 2 +W 66 2 diff --git a/contrib/visualize-alignment/guestfish-write-hello.qtr b/contrib/visualize-alignment/guestfish-write-hello.qtr new file mode 100644 index 0000000..b561d6d --- /dev/null +++ b/contrib/visualize-alignment/guestfish-write-hello.qtr @@ -0,0 +1,81 @@ +S 20480 +R 0 1 +R 0 8 +R 20352 8 +R 20464 8 +R 0 8 +R 8 8 +R 20472 8 +R 20216 8 +R 20416 8 +R 20224 8 +R 20080 8 +R 2048 8 +R 24 8 +R 56 8 +R 120 8 +R 16 8 +R 128 8 +R 64 8 +R 512 8 +R 32 8 +R 4096 8 +R 20288 8 +R 20400 8 +R 64 8 +R 72 8 +R 20416 1 +R 20160 1 +R 20161 7 +R 20408 8 +R 2112 8 +R 88 8 +R 120 8 +R 184 8 +R 80 8 +R 192 8 +R 128 8 +R 576 8 +R 96 8 +R 4160 8 +R 0 8 +R 0 32 +R 20288 1 +R 20400 1 +R 64 1 +R 72 1 +R 64 4 +R 64 4 +R 20288 8 +R 20400 8 +R 64 8 +R 72 8 +R 20416 1 +R 20160 8 +R 20408 8 +R 2112 8 +R 88 8 +R 120 8 +R 184 8 +R 80 8 +R 192 8 +R 128 8 +R 576 8 +R 96 8 +R 4160 8 +R 66 2 +R 68 2 +R 152 2 +W 66 2 +R 470 2 +R 150 2 +R 154 2 +R 148 2 +W 66 4 +W 148 2 +W 470 2 +W 13378 2 +W 150 6 +W 66 2 +W 66 2 +W 66 2 diff --git a/contrib/visualize-alignment/qemu-0.13-trace-block-device-access.patch b/contrib/visualize-alignment/qemu-0.13-trace-block-device-access.patch new file mode 100644 index 0000000..96904b3 --- /dev/null +++ b/contrib/visualize-alignment/qemu-0.13-trace-block-device-access.patch @@ -0,0 +1,104 @@ +From e04ef476fd330485e5a88c7018d29c55cf411fe2 Mon Sep 17 00:00:00 2001 +From: Richard W.M. Jones +Date: Tue, 5 Oct 2010 09:54:10 +0100 +Subject: [PATCH] Trace reads and writes to qemu block devices. + +NB: This patch is not suitable for nor intended to go upstream in +its current form. + +When qemu opens a block device, this patch creates a trace file +in /tmp with a name related to the block device. Reads and writes +to the device cause lines to be written to the trace file: + + S + W + R + +'S' is the summary line, printed first which just tells you how +many sectors are on the device. + +'W' and 'R' are writes and reads, for the range through +to + - 1. +--- + block.c | 29 +++++++++++++++++++++++++++++ + block_int.h | 3 +++ + 2 files changed, 32 insertions(+), 0 deletions(-) + +diff --git a/block.c b/block.c +index ebbc376..26ead5b 100644 +--- a/block.c ++++ b/block.c +@@ -474,6 +474,23 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename, + goto free_and_fail; + } + ++ /* Open trace file in /tmp based on filename. XXX */ ++ size_t len = strlen (filename); ++ char *trace_file = qemu_malloc (10 + len); ++ snprintf (trace_file, 10 + len, "/tmp/%s.qtr", filename); ++ size_t i; ++ for (i = 5; i < 5 + len; ++i) { ++ if (trace_file[i] == '/') ++ trace_file[i] = '_'; ++ } ++ bs->trace_fp = fopen (trace_file, "w"); ++ if (bs->trace_fp) { ++ setlinebuf (bs->trace_fp); ++ fprintf (bs->trace_fp, "S %" PRIi64 "\n", bs->total_sectors); ++ } else { ++ perror (trace_file); ++ } ++ + #ifndef _WIN32 + if (bs->is_temporary) { + unlink(filename); +@@ -665,6 +682,10 @@ void bdrv_close(BlockDriverState *bs) + bdrv_close(bs->file); + } + ++ if (bs->trace_fp) ++ fclose (bs->trace_fp); ++ bs->trace_fp = NULL; ++ + /* call the change callback */ + bs->media_changed = 1; + if (bs->change_cb) +@@ -1995,6 +2016,10 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, + /* Update stats even though technically transfer has not happened. */ + bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE; + bs->rd_ops ++; ++ ++ if (bs->trace_fp) ++ fprintf (bs->trace_fp, ++ "R %" PRIi64 " %d\n", sector_num, nb_sectors); + } + + return ret; +@@ -2028,6 +2053,10 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, + if (bs->wr_highest_sector < sector_num + nb_sectors - 1) { + bs->wr_highest_sector = sector_num + nb_sectors - 1; + } ++ ++ if (bs->trace_fp) ++ fprintf (bs->trace_fp, ++ "W %" PRIi64 " %d\n", sector_num, nb_sectors); + } + + return ret; +diff --git a/block_int.h b/block_int.h +index e8e7156..03e7c9b 100644 +--- a/block_int.h ++++ b/block_int.h +@@ -178,6 +178,9 @@ struct BlockDriverState { + uint64_t wr_ops; + uint64_t wr_highest_sector; + ++ /* Trace to file. */ ++ FILE *trace_fp; ++ + /* Whether the disk can expand beyond total_sectors */ + int growable; + +-- +1.7.3.1 + diff --git a/contrib/visualize-alignment/tracetops.ml b/contrib/visualize-alignment/tracetops.ml new file mode 100755 index 0000000..6600793 --- /dev/null +++ b/contrib/visualize-alignment/tracetops.ml @@ -0,0 +1,354 @@ +#!/usr/bin/ocamlrun /usr/bin/ocaml + +#use "topfind";; +#require "extlib";; + +(* Convert *.qtr (qemu block device trace) to Postscript. By Richard + * W.M. Jones . + * + * Note that we use ordinary OCaml ints, which means this program is + * limited to: ~1TB disks for 32 bit machines, or effectively unlimited + * for 64 bit machines. + *) + +open ExtList +open Scanf +open Printf + +type op = Read | Write + +(* If 'true' then print debug messages. *) +let debug = false + +(* Width of each row (in sectors) in the output. *) +let row_size = 64 + +(* Desirable alignment (sectors). *) +let alignment = 8 + +(* Height (in 1/72 inch) of the final image. *) +let height = 6.*.72. + +(* Width (in 1/72 inch) of the final image. *) +let width = 6.*.72. + +(* Reserve at left for the sector number (comes out of width). *) +let sn_width = 36. + +let input = + if Array.length Sys.argv = 2 then + Sys.argv.(1) + else + failwith "usage: tracetops filename.qtr" + +(* Read the input file. *) +let nb_sectors, accesses = + let chan = open_in input in + let nb_sectors = + let summary = input_line chan in + if String.length summary < 1 || summary.[0] <> 'S' then + failwith (sprintf "%s: input is not a qemu block device trace file" + input); + sscanf summary "S %d" (fun x -> x) in + + if nb_sectors mod row_size <> 0 then + failwith (sprintf "input nb_sectors (%d) not divisible by row size (%d)" + nb_sectors row_size); + + (* Read the reads and writes from the remainder of the file. *) + let accesses = ref [] in + let rec loop () = + let line = input_line chan in + let rw, s, n = sscanf line "%c %d %d" (fun rw s n -> (rw, s, n)) in + let rw = + match rw with + | 'R' -> Read | 'W' -> Write + | c -> failwith + (sprintf "%s: error reading input: got '%c', expecting 'R' or 'W'" + input c) in + if n < 0 || s < 0 || s+n > nb_sectors then + failwith (sprintf "%s: s (%d), n (%d) out of range" input s n); + let aligned = s mod alignment = 0 && n mod alignment = 0 in + accesses := (rw, aligned, s, n) :: !accesses; + loop () + in + (try loop () with + | End_of_file -> () + | Scan_failure msg -> + failwith (sprintf "%s: error reading input: %s" input msg) + ); + close_in chan; + + let accesses = List.rev !accesses in + + if debug then ( + eprintf "%s: nb_sectors = %d, accesses = %d\n" + input nb_sectors (List.length accesses) + ); + + nb_sectors, accesses + +let ranges = + (* Given the number of sectors, make the row array. *) + let nr_rows = nb_sectors / row_size in + let rows = Array.make nr_rows false in + + List.iter ( + fun (_, _, s, n) -> + let i0 = s / row_size in + let i1 = (s+n-1) / row_size in + for i = i0 to i1 do rows.(i) <- true done; + ) accesses; + + (* Coalesce rows into a list of ranges of rows we will draw. *) + let rows = Array.to_list rows in + let rows = List.mapi (fun i v -> (v, i)) rows in + let ranges = + (* When called, we are in the middle of a range which started at i0. *) + let rec loop i0 = function + | (false, _) :: (false, _) :: (true, i1) :: [] + | _ :: (_, i1) :: [] + | (_, i1) :: [] -> + [i0, i1] + | (false, _) :: (false, _) :: (true, _) :: rest + | (false, _) :: (true, _) :: rest + | (true, _) :: rest -> + loop i0 rest + | (false, i1) :: rest -> + let i1 = i1 - 1 in + let rest = List.dropwhile (function (v, _) -> not v) rest in + (match rest with + | [] -> [i0, i1] + | (_, i2) :: rest -> (i0, i1) :: loop i2 rest) + | [] -> assert false + in + loop 0 (List.tl rows) in + + if debug then ( + eprintf "%s: rows = %d (ranges = %d)\n" input nr_rows (List.length ranges); + List.iter ( + fun (i0, i1) -> + eprintf " %d - %d (rows %d - %d)\n" + (i0 * row_size) ((i1 + 1) * row_size - 1) i0 i1 + ) ranges + ); + + ranges + +(* Locate where we will draw the rows and cells in the final image. *) +let iter_rows, mapxy, row_height, cell_width = + let nr_ranges = List.length ranges in + let nr_breaks = nr_ranges - 1 in + let nr_rows = + List.fold_left (+) 0 (List.map (fun (i0,i1) -> i1-i0+1) ranges) in + let nr_rnb = nr_rows + nr_breaks in + let row_height = height /. float nr_rnb in + let cell_width = (width -. sn_width) /. float row_size in + + if debug then ( + eprintf "number of rows and breaks = %d\n" nr_rnb; + eprintf "row_height x cell_width = %g x %g\n" row_height cell_width + ); + + (* Create a higher-order function to iterate over the rows. *) + let rec iter_rows f = + let rec loop row = function + | [] -> () + | (i0,i1) :: rows -> + for i = i0 to i1 do + let y = float (row+i-i0) *. row_height in + f y (Some i) + done; + (* Call an extra time for the break. *) + let y = float (row+i1-i0+1) *. row_height in + if rows <> [] then f y None; + (* extra +1 here is to skip the break *) + loop (row+i1-i0+1+1) rows + in + loop 0 ranges + in + + (* Create a hash which maps from the row number to the position + * where we draw the row. If the row is not drawn, the hash value + * is missing. + *) + let row_y = Hashtbl.create nr_rows in + iter_rows ( + fun y -> + function + | Some i -> Hashtbl.replace row_y i y + | None -> () + ); + + (* Create a function which maps from the sector number to the final + * position that we will draw it. + *) + let mapxy s = + let r = s / row_size in + let y = try Hashtbl.find row_y r with Not_found -> assert false in + let x = sn_width +. cell_width *. float (s mod row_size) in + x, y + in + + iter_rows, mapxy, row_height, cell_width + +(* Start the PostScript file. *) +let () = + printf "%%!PS-Adobe-3.0 EPSF-3.0\n"; + printf "%%%%BoundingBox: -10 -10 %g %g\n" + (width +. 10.) (height +. row_height +. 20.); + printf "%%%%Creator: tracetops.ml (part of libguestfs)\n"; + printf "%%%%Title: %s\n" input; + printf "%%%%LanguageLevel: 2\n"; + printf "%%%%Pages: 1\n"; + printf "%%%%Page: 1 1\n"; + printf "\n"; + + printf "/min { 2 copy gt { exch } if pop } def\n"; + printf "/max { 2 copy lt { exch } if pop } def\n"; + + (* Function for drawing cells. *) + printf "/cell {\n"; + printf " newpath\n"; + printf " moveto\n"; + printf " %g 0 rlineto\n" cell_width; + printf " 0 %g rlineto\n" row_height; + printf " -%g 0 rlineto\n" cell_width; + printf " closepath\n"; + printf " gsave fill grestore 0.75 setgray stroke\n"; + printf "} def\n"; + + (* Define colours for different cell types. *) + printf "/unalignedread { 0.95 0.95 0 setrgbcolor } def\n"; + printf "/unalignedwrite { 0.95 0 0 setrgbcolor } def\n"; + printf "/alignedread { 0 0.95 0 setrgbcolor } def\n"; + printf "/alignedwrite { 0 0 0.95 setrgbcolor } def\n"; + + (* Get width of text. *) + printf "/textwidth { stringwidth pop } def\n"; + + (* Draw the outline. *) + printf "/outline {\n"; + printf " newpath\n"; + printf " %g 0 moveto\n" sn_width; + printf " %g 0 lineto\n" width; + printf " %g %g lineto\n" width height; + printf " %g %g lineto\n" sn_width height; + printf " closepath\n"; + printf " 0.5 setlinewidth 0.3 setgray stroke\n"; + printf "} def\n"; + + (* Draw the outline breaks. *) + printf "/breaks {\n"; + iter_rows ( + fun y -> + function + | Some _ -> () + | None -> + let f xmin xmax = + let yll = y +. row_height /. 3. -. 2. in + let ylr = y +. row_height /. 2. -. 2. in + let yur = y +. 2. *. row_height /. 3. in + let yul = y +. row_height /. 2. in + printf " newpath\n"; + printf " %g %g moveto\n" xmin yll; + printf " %g %g lineto\n" xmax ylr; + printf " %g %g lineto\n" xmax yur; + printf " %g %g lineto\n" xmin yul; + printf " closepath\n"; + printf " 1 setgray fill\n"; + printf " newpath\n"; + printf " %g %g moveto\n" xmin yll; + printf " %g %g lineto\n" xmax ylr; + printf " %g %g moveto\n" xmax yur; + printf " %g %g lineto\n" xmin yul; + printf " closepath\n"; + printf " 0.5 setlinewidth 0.3 setgray stroke\n" + in + f (sn_width -. 6.) (sn_width +. 6.); + f (width -. 6.) (width +. 6.) + ); + printf "} def\n"; + + (* Draw the labels. *) + printf "/labels {\n"; + printf " /Courier findfont\n"; + printf " 0.75 %g mul 10 min scalefont\n" row_height; + printf " setfont\n"; + iter_rows ( + fun y -> + function + | Some i -> + let sector = i * row_size in + printf " newpath\n"; + printf " /s { (%d) } def\n" sector; + printf " %g s textwidth sub 4 sub %g moveto\n" sn_width (y +. 2.); + printf " s show\n" + | None -> () + ); + printf "} def\n"; + + (* Print the key. *) + printf "/key {\n"; + printf " /Times-Roman findfont\n"; + printf " 10. scalefont\n"; + printf " setfont\n"; + let x = sn_width and y = height +. 10. in + printf " unalignedwrite %g %g cell\n" x y; + let x = x +. cell_width +. 4. in + printf " newpath %g %g moveto (unaligned write) 0.3 setgray show\n" x y; + let x = x +. 72. in + printf " unalignedread %g %g cell\n" x y; + let x = x +. cell_width +. 4. in + printf " newpath %g %g moveto (unaligned read) 0.3 setgray show\n" x y; + let x = x +. 72. in + printf " alignedwrite %g %g cell\n" x y; + let x = x +. cell_width +. 4. in + printf " newpath %g %g moveto (aligned write) 0.3 setgray show\n" x y; + let x = x +. 72. in + printf " alignedread %g %g cell\n" x y; + let x = x +. cell_width +. 4. in + printf " newpath %g %g moveto (aligned read) 0.3 setgray show\n" x y; + printf "} def\n"; + + printf "\n" + +(* Draw the accesses. *) +let () = + (* Sort the accesses so unaligned ones are displayed at the end (on + * top of aligned ones) and writes on top of reads. This isn't + * really perfect, but it'll do. + *) + let cmp (rw, aligned, s, n) (rw', aligned', s', n') = + let r = compare rw rw' (* Write later *) in + if r <> 0 then r else ( + let r = compare aligned' aligned (* unaligned later *) in + if r <> 0 then r else + compare (n, s) (n', s') + ) + in + let accesses = List.sort ~cmp accesses in + + List.iter ( + fun op -> + let col, s, n = + match op with + | Read, false, s, n -> + "unalignedread", s, n + | Write, false, s, n -> + "unalignedwrite", s, n + | Read, true, s, n -> + "alignedread", s, n + | Write, true, s, n -> + "alignedwrite", s, n in + for i = s to s+n-1 do + let x, y = mapxy i in + printf "%s %g %g cell\n" col x y + done; + printf "\n" + ) accesses + +(* Finish off the PostScript output. *) +let () = + printf "outline breaks labels key\n"; + printf "%%%%EOF\n"