1# 2# Taken from /usr/share/systemtap/tapset/regex.stp and registers.stp 3# 4// Regular expression subexpression tapset 5// Copyright (C) 2017 Serhei Makarov 6// Copyright (C) 2013 Red Hat, Inc. 7// 8// This file is part of systemtap, and is free software. You can 9// redistribute it and/or modify it under the terms of the GNU General 10// Public License (GPL); either version 2, or (at your option) any 11// later version. 12 13%{ 14#define STAP_NEED_CONTEXT_SUBEXPRESSION 1 15%} 16 17/** 18 * sfunction matched_str - Return the last matched string. 19 * 20 * Description: returns the string matched by the last successful 21 * use of the =~ regexp matching operator. Returns an error if the 22 * last use of =~ led to a failed match. 23 */ 24function matched_str:string() { return matched(0) } 25 26/** 27 * sfunction matched - Return a given matched subexpression. 28 * 29 * @n: index to the subexpression to return. 0 corresponds to the 30 * entire regular expression. 31 * 32 * Description: returns the content of the n'th subexpression of the 33 * last successful use of the =~ regex matching operator. Returns an 34 * empty string if the n'th subexpression was not matched (e.g. due to 35 * alternation). Throws an error if the last use of =~ was a failed 36 * match, or if fewer than n subexpressions are present in the 37 * original regexp. 38 */ 39function matched:string(n:long) 40%{ /* pure */ /* unprivileged */ /* pragma:tagged_dfa */ 41 int start_ix, end_ix; // indices into tag buffer 42 int start, end, length; // actual coordinate values 43 44 if (!CONTEXT->last_match.result) { 45 snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), 46 "Attempted to get subexpression %lld from failed match", (long long) STAP_ARG_n); 47 CONTEXT->last_error = CONTEXT->error_buffer; 48 } 49 50 start_ix = 2 * STAP_ARG_n, end_ix = start_ix + 1; 51 52 if (end_ix >= CONTEXT->last_match.num_final_tags) { 53 snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), 54 "Attempted to get nonexistent subexpression %lld", (long long) STAP_ARG_n); 55 CONTEXT->last_error = CONTEXT->error_buffer; 56 } 57 58 start = CONTEXT->last_match.tag_vals[start_ix]; 59 end = CONTEXT->last_match.tag_vals[end_ix]; 60 // _stp_printf ("**DEBUG** Extracted subexpression #%lld:(%d,%d) from %d to %d\n", STAP_ARG_n, start_ix, end_ix, start, end); 61 62 if (start < 0 || end < 0) { 63 // If indices are negative, the group was not matched. Return empty string: 64 start = end = 0; 65 // snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), 66 // "Unknown coordinates for subexpression %lld", STAP_ARG_n); 67 // CONTEXT->last_error = CONTEXT->error_buffer; 68 } 69 70 if (start > end) { 71 // This should not happen. 72 snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), 73 "BUG: inverted coordinates for subexpression %lld", (long long) STAP_ARG_n); 74 CONTEXT->last_error = CONTEXT->error_buffer; 75 } 76 77 length = end - start; 78 79 // TODOXXX assert (start <= strlen(matched_str)) ?? 80 // XXX: Must add 1 to length to account for NUL byte in strlcpy(). 81 strlcpy(STAP_RETVALUE, CONTEXT->last_match.matched_str + start, length + 1); 82%} 83 84/** 85 * sfunction ngroups - Number of subexpressions in the last match. 86 * 87 * Description: returns the number of subexpressions from the 88 * last successful use of the =~ regex matching operator. 89 * 90 * Note that this number includes subexpressions which are present in 91 * the regex but did not match any string; for example, given the 92 * regex "a|(b)", the subexpressions will count the group for (b) 93 * regardless of whether it matched a string or not. Throws an error 94 * if the last use of =~ was a failed match. 95 */ 96function ngroups:long() 97%{ /* pure */ /* unprivileged */ /* pragma:tagged_dfa */ 98 if (!CONTEXT->last_match.result) { 99 snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer), 100 "Attempted to get subexpression count from failed match"); 101 CONTEXT->last_error = CONTEXT->error_buffer; 102 } 103 104 STAP_RETVALUE = CONTEXT->last_match.num_final_tags / 2; 105%} 106 107// XXX: perhaps implement matched_start, matched_end to get indices? 108// XXX: some kind of find-replace functionality? 109// XXX: some kind of splitting / multiple-match functionality? 110 111/* 112 * In nd_syscall.return probes, we sometimes need @entry() values. To 113 * ensure we get the argument in the correct mode, we need a function 114 * that calls asmlinkage() first. 115 */ 116function __asmlinkage_int_arg:long(n:long) 117{ 118 asmlinkage() 119 return int_arg(n) 120} 121