Repository navigation
[tools] Add mlogselect tool #1973
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| (****************************************************************************) | ||
| (* the diy toolsuite *) | ||
| (* *) | ||
| (* Jade Alglave, University College London, UK. *) | ||
| (* Luc Maranget, INRIA Paris-Rocquencourt, France. *) | ||
| (* *) | ||
| (* Copyright 2026-present Institut National de Recherche en Informatique et *) | ||
| (* en Automatique and the authors. All rights reserved. *) | ||
| (* *) | ||
| (* This software is governed by the CeCILL-B license under French law and *) | ||
| (* abiding by the rules of distribution of free software. You can use, *) | ||
| (* modify and/ or redistribute the software under the terms of the CeCILL-B *) | ||
| (* license as circulated by CEA, CNRS and INRIA at the following URL *) | ||
| (* "http://www.cecill.info". We also give a copy in LICENSE.txt. *) | ||
| (****************************************************************************) | ||
|
|
||
| (** Fast lexing of logs, for selection *) | ||
|
|
||
| val from_chan : (string -> bool) -> in_channel -> unit | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| (****************************************************************************) | ||
| (* the diy toolsuite *) | ||
| (* *) | ||
| (* Jade Alglave, University College London, UK. *) | ||
| (* Luc Maranget, INRIA Paris-Rocquencourt, France. *) | ||
| (* *) | ||
| (* Copyright 2026-present Institut National de Recherche en Informatique et *) | ||
| (* en Automatique and the authors. All rights reserved. *) | ||
| (* *) | ||
| (* This software is governed by the CeCILL-B license under French law and *) | ||
| (* abiding by the rules of distribution of free software. You can use, *) | ||
| (* modify and/ or redistribute the software under the terms of the CeCILL-B *) | ||
| (* license as circulated by CEA, CNRS and INRIA at the following URL *) | ||
| (* "http://www.cecill.info". We also give a copy in LICENSE.txt. *) | ||
| (****************************************************************************) | ||
|
|
||
| (** Extract records from herd/litmus/msum logs *) | ||
|
|
||
| (* | ||
| * The extraction is a bit complicated by the variety of log formats... | ||
| * + Fortunately all records starts with `Test `<name>, where <name> | ||
| * is the name of the test. | ||
| * + Unfortunately, the format of record end differ: | ||
| * - The records produced by herd and msum end at the first empty line. | ||
| * - The records produced by litmus include an empty line that' | ||
| * follows the validation tag (Ok/No). The end at the "Time" | ||
| * information that always follow the Hash metadata. | ||
|
Comment on lines
+20
to
+27
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we not, instead, change I also think some of the complexity arises from the fact that tools like $ cat results.jsonl
{"name":"Test1", "states":[{...},{...}], ...}
{"name":"Test2", "states":[], ...}
# select logs by name (similar to mlogselect7)
$ jq 'select(.name == "Test2")' results.jsonl
{
"name": "Test2",
"states": [],
...
}
# select logs with at least one outcome state (similar to mlog2name7)
$ jq -r 'select(.states | length > 0) | .name' results.jsonl
Test1
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slighty changing the log format looks a very good idea. What can easily be done is:
I remain attached to readable text format for logs. I want to be able to read logs without the mediation of a tool. But of course, we should facilitate the processing of logs. In my opinion,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I've been thinking about a logging module, that can be used to centralize decisions regarding loglevel, as well as verbosity. My main concern was to reduce the number of functors since logging is per-process usually instead of per-module. Regardless of my initial purpose, this could be used as an opportunity to start generating structured logging and easily select the format of the logging, using different formatters. There's a nice library available in the ecosystem that allows this kind of separation of concerns: https://erratique.ch/software/logs Would people be open to this kind change to the codebase?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note that "machine-readable" doesn't necessarily imply "hard to read by humans". E.g. YAML is a common machine-readable format that is also quite nice to read by humans, IMO. Moreover the machine-readable format doesn't need to (and shouldn't) replace the existing readable one; it could be put behind an opt-in flag I think my overall point is that if we need tools to routinely consume logs, it's a good idea to make the log format easy to process, and ideally leverage existing standards and tools if possible. Whether that is achieved by making the current format more machine-readable, or by offering an option like
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note, in fact, that |
||
| * The output of mlogselect adopts the convention of herd and msum. | ||
| *) | ||
| { | ||
| open LexMisc | ||
|
|
||
| let outline = print_endline | ||
|
|
||
| type st = { inside:bool; hash_seen:bool; } | ||
| let st_false = { inside=false; hash_seen=false; } | ||
| let st_true = { inside=true; hash_seen=false; } | ||
| } | ||
|
|
||
| let digit = [ '0'-'9' ] | ||
| let alpha = [ 'a'-'z' 'A'-'Z'] | ||
| let name = (alpha|'_'|'.'|'$') (alpha|digit|'_'| '.')* | ||
| let blank = [' ' '\t'] | ||
| let testname = (alpha|digit|'_' | '/' | '.' | '-' | '+' | '[' | ']' | ':')+ | ||
| let nl = '\r'?'\n' | ||
| let validation = "Ok"|"No" | ||
|
|
||
| rule main ok st = parse | ||
| | "Test" blank+ (testname as t) | ||
| (blank+ name)? as line nl | ||
| { incr_lineno lexbuf ; | ||
| let st = if ok t then st_true else st_false in | ||
| if st.inside then outline line ; | ||
| main ok st lexbuf } | ||
| | nl (* An empty line ends one test log in herd/msum logs *) | ||
| { incr_lineno lexbuf ; | ||
| if st.inside then outline "" ; | ||
| main ok st_false lexbuf } | ||
| | validation as line nl nl | ||
| (* Unless the empty line follows a validation tag, | ||
| as in some litmus logs *) | ||
| { incr_lineno lexbuf ; incr_lineno lexbuf ; | ||
| if st.inside then outline line ; | ||
| main ok st lexbuf } | ||
| | ['h''H']['a''A']['s''S']['h''H'] | ||
| blank* '=' blank* [^' ''\t''\n''\r']+ blank* as line nl | ||
| (* Detect hash metadata *) | ||
| { incr_lineno lexbuf ; | ||
| if st.inside then outline line ; | ||
| main ok {st with hash_seen=true; } lexbuf } | ||
| | "Time" blank+ testname blank+ (digit|'.')+ blank* as line nl | ||
| (* Detect timing information that ends records in litmus logs *) | ||
| { incr_lineno lexbuf ; | ||
| if st.inside then outline line ; | ||
| if st.hash_seen then begin (* End of record, if follows hash metadata *) | ||
| if st.inside then outline "" ; (* Add empty line *) | ||
| main ok st_false lexbuf | ||
| end else | ||
| main ok st lexbuf } | ||
| | [^'\n''\r']+ as line nl | ||
| { incr_lineno lexbuf ; | ||
| if st.inside then outline line ; | ||
| main ok st lexbuf } | ||
| | [^'\n''\r']* as line eof | ||
| { if st.inside && line <> "" then outline line } | ||
|
|
||
| { | ||
|
|
||
| let from_chan ok chan = main ok st_false @@ Lexing.from_channel chan | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| (****************************************************************************) | ||
| (* the diy toolsuite *) | ||
| (* *) | ||
| (* Jade Alglave, University College London, UK. *) | ||
| (* Luc Maranget, INRIA Paris-Rocquencourt, France. *) | ||
| (* *) | ||
| (* Copyright 2026-present Institut National de Recherche en Informatique et *) | ||
| (* en Automatique and the authors. All rights reserved. *) | ||
| (* *) | ||
| (* This software is governed by the CeCILL-B license under French law and *) | ||
| (* abiding by the rules of distribution of free software. You can use, *) | ||
| (* modify and/ or redistribute the software under the terms of the CeCILL-B *) | ||
| (* license as circulated by CEA, CNRS and INRIA at the following URL *) | ||
| (* "http://www.cecill.info". We also give a copy in LICENSE.txt. *) | ||
| (****************************************************************************) | ||
|
|
||
| (* Extract some records from logs. *) | ||
| open Printf | ||
| open OptNames | ||
|
|
||
| let prog = | ||
| if Array.length Sys.argv > 0 then Sys.argv.(0) | ||
| else "mlogelect" | ||
|
|
||
| let arg = ref None | ||
|
|
||
| let () = | ||
| Arg.parse | ||
| OptNames.parse_withselect | ||
| (fun s -> | ||
| arg := | ||
| match !arg with | ||
| | None -> Some s | ||
| | Some _ -> raise (Arg.Bad "one argument at most")) | ||
| (sprintf "usage: %s [options]* log?" prog) | ||
|
|
||
| (* Read names *) | ||
|
|
||
| module Check = | ||
| CheckName.Make | ||
| (struct | ||
| let verbose = 0 | ||
| let rename = [] | ||
| let select = !select | ||
| let names = !names | ||
| let oknames = !oknames | ||
| let excl = !excl | ||
| let nonames = !nonames | ||
| end) | ||
|
|
||
| let zyva () = | ||
| match !arg with | ||
| | None -> | ||
| LexLogSelect.from_chan Check.ok stdin | ||
| | Some f -> | ||
| Misc.input_protect | ||
| (LexLogSelect.from_chan Check.ok) | ||
| f | ||
|
|
||
| let () = | ||
| try zyva () | ||
| with | ||
| | Misc.(Fatal msg|UserError msg) -> | ||
| Warn.warn_always "Fatal error: %s" msg | ||
| | _ -> assert false |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| (cram | ||
| (deps | ||
| %{bin:mlogselect7} | ||
| %{bin:msum7} | ||
| %{bin:herd7} | ||
| (glob_files %{workspace_root}/herd/tests/instructions/X86_64/*.litmus) | ||
| (glob_files %{workspace_root}/doc/SB-PPC.litmus) | ||
| (glob_files %{workspace_root}/doc/SB-PPC*.log) | ||
| (source_tree %{workspace_root}/herd/libdir))) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| Extract test output from litmus log | ||
| $ mlogselect7 -oknames SB-PPC ../../../doc/SB-PPC.log | ||
| Test SB-PPC Allowed | ||
| Histogram (3 states) | ||
| 1784 *>0:r3=0; 1:r3=0; | ||
| 498564:>0:r3=1; 1:r3=0; | ||
| 499652:>0:r3=0; 1:r3=1; | ||
| Ok | ||
| Witnesses | ||
| Positive: 1784, Negative: 998216 | ||
| Condition exists (0:r3=0 /\ 1:r3=0) is validated | ||
| Hash=4edecf6abc507611612efaecc1c4a9bc | ||
| Observation SB-PPC Sometimes 1784 998216 | ||
| Time SB-PPC 0.55 | ||
|
|
||
| Extract test output from msum log | ||
| $ msum7 ../../../doc/SB-PPC*.log 2>/dev/null | mlogselect7 -select ../../../doc/SB-PPC.litmus | ||
| Test SB-PPC Allow | ||
| Histogram (3 states) | ||
| 3549 :> 0:r3=0; 1:r3=0; | ||
| 999146 :> 0:r3=0; 1:r3=1; | ||
| 997305 :> 0:r3=1; 1:r3=0; | ||
| Ok | ||
| Witnesses | ||
| Positive: 3549 Negative: 1996451 | ||
| Condition exists (0:r3=0 /\ 1:r3=0) is validated | ||
| Hash=4edecf6abc507611612efaecc1c4a9bc | ||
| Time SB-PPC 1.12 | ||
|
|
||
| Extract test output from herd log | ||
| $ for i in $(seq 1 2 9); do echo "A00$i"; done > NAMES | ||
| $ herd7 -set-libdir ../../../herd/libdir ../../../herd/tests/instructions/X86_64/*.litmus | mlogselect7 -names NAMES -nonames A003,A004 -oknames A012 | ||
| Test A001 Required | ||
| States 1 | ||
| 0:rip=0; | ||
| Ok | ||
| Witnesses | ||
| Positive: 1 Negative: 0 | ||
| Condition forall (0:rip=0) | ||
| Observation A001 Always 1 0 | ||
| Time A001 0.00 | ||
| Hash=5586d6c213112d3683c915b4d7bb700a | ||
|
|
||
| Test A005 Required | ||
| States 1 | ||
| 0:rax=0; | ||
| Ok | ||
| Witnesses | ||
| Positive: 1 Negative: 0 | ||
| Condition forall (0:rax=0) | ||
| Observation A005 Always 1 0 | ||
| Time A005 0.00 | ||
| Hash=33e970980643a03ccf92e4989259cd01 | ||
|
|
||
| Test A007 Required | ||
| States 1 | ||
| 0:rax=4; | ||
| Ok | ||
| Witnesses | ||
| Positive: 1 Negative: 0 | ||
| Condition forall (0:rax=4) | ||
| Observation A007 Always 1 0 | ||
| Time A007 0.00 | ||
| Hash=a335aafc9060d9b8c4320424ba909740 | ||
|
|
||
| Test A009 Required | ||
| States 1 | ||
| 0:rcx=-1; | ||
| Ok | ||
| Witnesses | ||
| Positive: 1 Negative: 0 | ||
| Condition forall (0:rcx=-1) | ||
| Observation A009 Always 1 0 | ||
| Time A009 0.00 | ||
| Hash=7ca3c35015d75a877ccf509d75062e79 | ||
|
|
||
| Test A012 Required | ||
| States 1 | ||
| [x]=1; | ||
| Ok | ||
| Witnesses | ||
| Positive: 1 Negative: 0 | ||
| Condition forall ([x]=1) | ||
| Observation A012 Always 1 0 | ||
| Time A012 0.00 | ||
| Hash=3b021e40517dcff1f1cb8894d645b677 | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this lexing module is doing quite a bit more than just lexing, as it currently implements the whole tool's functionality including mutable state updates and stdout printing.
Could we separate lexing from log selection and output? I think it would be cleaner if the lexer only classified tokens/lines, with a separate module/function taking care of log filtering and printing.
I would also like to point out that this module adds another lexer/parser alongside existing modules like
lexLog_tools,lexHashLog, etc., which, from a quick look, seem to all implement their own slightly different parsing logic for the same log format. Is there a reason for this apparent duplication? Could we instead implement a single lexer/parser for log files that is shared among all the log-processing tools?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having one pass tools is old fashioned, I admit. The task at hand here remains simple: identify begin and end markers, extract test name. In my old-fashioned (outdated, maybe) view, a simple tool that does everything is adequate.
We could do that, and would be more robust against log format evolution. However, this is more involved than writing the simple tool I have needed to select offending test outputs while debugging PR #1970....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you clarify what you mean by "one pass"? Is this about reading the input file once?
Of course, it doesn't need to happen now! Though perhaps I would add a "TODO" comment and/or create a Github ticket about it so that we don't forget.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum not exactly one pass, I meant a tool that acts incrementally while reading its input, by opposion with a tool that would read the file completely before performing any specific work.