Skip to content

Commit 94840fd

Browse files
committed
fix(cksum): add debug option
1 parent 690405d commit 94840fd

4 files changed

Lines changed: 197 additions & 1 deletion

File tree

src/uu/cksum/locales/en-US.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ cksum-help-raw = emit a raw binary digest, not hexadecimal
2222
cksum-help-strict = exit non-zero for improperly formatted checksum lines
2323
cksum-help-check = read hashsums from the FILEs and check them
2424
cksum-help-base64 = emit a base64 digest, not hexadecimal
25+
cksum-help-debug = indicate which implementation is used
2526
cksum-help-warn = warn about improperly formatted checksum lines
2627
cksum-help-status = don't output anything, status code shows success
2728
cksum-help-quiet = don't print OK for each successfully verified file

src/uu/cksum/locales/fr-FR.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ cksum-help-raw = émettre un condensé binaire brut, pas hexadécimal
2222
cksum-help-strict = sortir avec un code non-zéro pour les lignes de somme de contrôle mal formatées
2323
cksum-help-check = lire les sommes de hachage des FICHIERs et les vérifier
2424
cksum-help-base64 = émettre un condensé base64, pas hexadécimal
25+
cksum-help-debug = indiquer quelle implémentation est utilisée
2526
cksum-help-warn = avertir des lignes de somme de contrôle mal formatées
2627
cksum-help-status = ne rien afficher, le code de statut indique le succès
2728
cksum-help-quiet = ne pas afficher OK pour chaque fichier vérifié avec succès

src/uu/cksum/src/cksum.rs

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
// spell-checker:ignore (ToDO) fname, algo
6+
// spell-checker:ignore (ToDO) fname, algo, hwcaps, pclmul, vmull, pmull, vpclmulqdq, pclmulqdq, tunables, behaviour
77

88
use clap::builder::ValueParser;
99
use clap::{Arg, ArgAction, Command};
10+
use std::collections::HashSet;
11+
use std::env;
1012
use std::ffi::{OsStr, OsString};
1113
use std::fs::File;
1214
use std::io::{BufReader, Read, Write, stdin, stdout};
@@ -37,6 +39,7 @@ struct Options {
3739
length: Option<usize>,
3840
output_format: OutputFormat,
3941
line_ending: LineEnding,
42+
debug: bool,
4043
}
4144

4245
/// Reading mode used to compute digest.
@@ -188,6 +191,8 @@ where
188191
{
189192
let mut files = files.peekable();
190193

194+
emit_crc_debug_info(&options);
195+
191196
while let Some(filename) = files.next() {
192197
// Check that in raw mode, we are not provided with several files.
193198
if options.output_format.is_raw() && files.peek().is_some() {
@@ -268,6 +273,160 @@ where
268273
Ok(())
269274
}
270275

276+
fn log_debug_message(feature: &str, status: Option<bool>) {
277+
match status {
278+
Some(true) => eprintln!("cksum: using {feature} hardware support"),
279+
Some(false) => eprintln!("cksum: {feature} support not detected"),
280+
None => {}
281+
}
282+
}
283+
284+
/// Emit GNU-compatible hardware selection messages for `cksum --debug`.
285+
///
286+
/// GNU cksum prints which SIMD/SW implementation it picked whenever `--debug` is present.
287+
/// The upstream tests rely on that output to ensure every hardware-specific path can be
288+
/// exercised (including cases where GLIBC_TUNABLES forces a feature off). We mirror that
289+
/// behaviour here by checking both `std::arch` feature flags and the `glibc.cpu.hwcaps`
290+
/// overrides, but only for the legacy CRC algorithms where multiple implementations exist.
291+
fn emit_crc_debug_info(options: &Options) {
292+
if !options.debug {
293+
return;
294+
}
295+
296+
if options.algo_name != ALGORITHM_OPTIONS_CRC && options.algo_name != ALGORITHM_OPTIONS_CRC32B {
297+
return;
298+
}
299+
300+
let disabled_caps = glibc_disabled_hwcaps();
301+
302+
log_debug_message("avx512", detect_avx512(&disabled_caps));
303+
log_debug_message("avx2", detect_avx2(&disabled_caps));
304+
log_debug_message("pclmul", detect_pclmul(&disabled_caps));
305+
306+
if options.algo_name == ALGORITHM_OPTIONS_CRC {
307+
log_debug_message("vmull", detect_vmull(&disabled_caps));
308+
}
309+
}
310+
311+
fn detect_avx512(disabled_caps: &HashSet<String>) -> Option<bool> {
312+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
313+
{
314+
if disabled_caps.contains("AVX512F")
315+
|| disabled_caps.contains("AVX512BW")
316+
|| disabled_caps.contains("VPCLMULQDQ")
317+
{
318+
return Some(false);
319+
}
320+
321+
Some(
322+
std::arch::is_x86_feature_detected!("avx512f")
323+
&& std::arch::is_x86_feature_detected!("avx512bw")
324+
&& std::arch::is_x86_feature_detected!("vpclmulqdq"),
325+
)
326+
}
327+
328+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
329+
{
330+
let _ = disabled_caps;
331+
None
332+
}
333+
}
334+
335+
fn detect_avx2(disabled_caps: &HashSet<String>) -> Option<bool> {
336+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
337+
{
338+
if disabled_caps.contains("AVX2") || disabled_caps.contains("VPCLMULQDQ") {
339+
return Some(false);
340+
}
341+
342+
Some(
343+
std::arch::is_x86_feature_detected!("avx2")
344+
&& std::arch::is_x86_feature_detected!("vpclmulqdq"),
345+
)
346+
}
347+
348+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
349+
{
350+
let _ = disabled_caps;
351+
None
352+
}
353+
}
354+
355+
fn detect_pclmul(disabled_caps: &HashSet<String>) -> Option<bool> {
356+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
357+
{
358+
if disabled_caps.contains("AVX")
359+
|| disabled_caps.contains("PCLMUL")
360+
|| disabled_caps.contains("PCLMULQDQ")
361+
{
362+
return Some(false);
363+
}
364+
365+
Some(
366+
std::arch::is_x86_feature_detected!("avx")
367+
&& std::arch::is_x86_feature_detected!("pclmulqdq"),
368+
)
369+
}
370+
371+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
372+
{
373+
let _ = disabled_caps;
374+
None
375+
}
376+
}
377+
378+
fn detect_vmull(disabled_caps: &HashSet<String>) -> Option<bool> {
379+
#[cfg(target_arch = "aarch64")]
380+
{
381+
if disabled_caps.contains("PMULL") {
382+
return Some(false);
383+
}
384+
385+
Some(std::arch::is_aarch64_feature_detected!("pmull"))
386+
}
387+
388+
#[cfg(not(target_arch = "aarch64"))]
389+
{
390+
let _ = disabled_caps;
391+
None
392+
}
393+
}
394+
395+
fn glibc_disabled_hwcaps() -> HashSet<String> {
396+
env::var("GLIBC_TUNABLES")
397+
.ok()
398+
.map(|value| parse_disabled_hwcaps(&value))
399+
.unwrap_or_default()
400+
}
401+
402+
fn parse_disabled_hwcaps(tunables: &str) -> HashSet<String> {
403+
let mut disabled = HashSet::new();
404+
405+
for entry in tunables.split(':') {
406+
let trimmed = entry.trim();
407+
let Some(value) = trimmed.strip_prefix("glibc.cpu.hwcaps=") else {
408+
continue;
409+
};
410+
411+
for token in value.split(',') {
412+
let token = token.trim();
413+
if token.is_empty() {
414+
continue;
415+
}
416+
417+
if let Some(feature) = token.strip_prefix('-') {
418+
let feature = feature.trim();
419+
420+
if !feature.is_empty() {
421+
disabled.insert(feature.to_ascii_uppercase());
422+
}
423+
}
424+
}
425+
}
426+
427+
disabled
428+
}
429+
271430
mod options {
272431
pub const ALGORITHM: &str = "algorithm";
273432
pub const FILE: &str = "file";
@@ -285,6 +444,7 @@ mod options {
285444
pub const IGNORE_MISSING: &str = "ignore-missing";
286445
pub const QUIET: &str = "quiet";
287446
pub const ZERO: &str = "zero";
447+
pub const DEBUG: &str = "debug";
288448
}
289449

290450
/// cksum has a bunch of legacy behavior. We handle this in this function to
@@ -395,6 +555,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
395555
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
396556

397557
let check = matches.get_flag(options::CHECK);
558+
let debug_flag = matches.get_flag(options::DEBUG);
398559

399560
let algo_cli = matches
400561
.get_one::<String>(options::ALGORITHM)
@@ -470,6 +631,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
470631
length,
471632
output_format,
472633
line_ending,
634+
debug: debug_flag,
473635
};
474636

475637
cksum(opts, files)?;
@@ -549,6 +711,12 @@ pub fn uu_app() -> Command {
549711
// GNU cksum does not permit these flags to be combined:
550712
.conflicts_with(options::RAW),
551713
)
714+
.arg(
715+
Arg::new(options::DEBUG)
716+
.long(options::DEBUG)
717+
.help(translate!("cksum-help-debug"))
718+
.action(ArgAction::SetTrue),
719+
)
552720
.arg(
553721
Arg::new(options::TEXT)
554722
.long(options::TEXT)
@@ -602,3 +770,24 @@ pub fn uu_app() -> Command {
602770
)
603771
.after_help(translate!("cksum-after-help"))
604772
}
773+
774+
#[cfg(test)]
775+
mod tests {
776+
use super::parse_disabled_hwcaps;
777+
778+
#[test]
779+
fn parse_disabled_hwcaps_picks_up_multiple_features() {
780+
let caps = parse_disabled_hwcaps("glibc.cpu.hwcaps=-AVX2,-PMULL,");
781+
assert!(caps.contains("AVX2"));
782+
assert!(caps.contains("PMULL"));
783+
}
784+
785+
#[test]
786+
fn parse_disabled_hwcaps_ignores_other_entries() {
787+
let caps =
788+
parse_disabled_hwcaps("glibc.malloc.trim=0:glibc.cpu.hwcaps=-AVX512F,-VPCLMULQDQ");
789+
assert!(caps.contains("AVX512F"));
790+
assert!(caps.contains("VPCLMULQDQ"));
791+
assert!(!caps.contains("MALLOC"));
792+
}
793+
}

tests/by-util/test_cksum.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ fn test_invalid_arg() {
2121
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
2222
}
2323

24+
#[test]
25+
fn test_debug_flag_is_accepted() {
26+
new_ucmd!().arg("--debug").arg("lorem_ipsum.txt").succeeds();
27+
}
28+
2429
#[test]
2530
fn test_single_file() {
2631
new_ucmd!()

0 commit comments

Comments
 (0)