mirror of
https://github.com/torvalds/linux.git
synced 2024-11-01 13:03:25 +01:00
06dd4c5a56
Currently, the perf tool infrastructure uses the disasm_line__parse function to parse disassembled line. Example snippet from objdump: objdump --start-address=<address> --stop-address=<address> -d --no-show-raw-insn -C <vmlinux> c0000000010224b4: lwz r10,0(r9) This line "lwz r10,0(r9)" is parsed to extract instruction name, registers names and offset. In powerpc, the approach for data type profiling uses raw instruction instead of result from objdump to identify the instruction category and extract the source/target registers. Example: 38 01 81 e8 ld r4,312(r1) Here "38 01 81 e8" is the raw instruction representation. Add function "disasm_line__parse_powerpc" to handle parsing of raw instruction. Also update "struct disasm_line" to save the binary code/ With the change, function captures: line -> "38 01 81 e8 ld r4,312(r1)" raw instruction "38 01 81 e8" Raw instruction is used later to extract the reg/offset fields. Macros are added to extract opcode and register fields. "struct disasm_line" is updated to carry union of "bytes" and "raw_insn" of 32 bit to carry raw code (raw). Function "disasm_line__parse_powerpc fills the raw instruction hex value and can use macros to get opcode. There is no changes in existing code paths, which parses the disassembled code. The size of raw instruction depends on architecture. In case of powerpc, the parsing the disasm line needs to handle cases for reading binary code directly from DSO as well as parsing the objdump result. Hence adding the logic into separate function instead of updating "disasm_line__parse". The architecture using the instruction name and present approach is not altered. Since this approach targets powerpc, the macro implementation is added for powerpc as of now. Since the disasm_line__parse is used in other cases (perf annotate) and not only data tye profiling, the powerpc callback includes changes to work with binary code as well as mnemonic representation. Also in case if the DSO read fails and libcapstone is not supported, the approach fallback to use objdump as option. Hence as option, patch has changes to ensure objdump option also works well. Reviewed-by: Kajol Jain <kjain@linux.ibm.com> Reviewed-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Athira Rajeev <atrajeev@linux.vnet.ibm.com> Tested-by: Kajol Jain <kjain@linux.ibm.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Akanksha J N <akanksha@linux.ibm.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Disha Goel <disgoel@linux.vnet.ibm.com> Cc: Hari Bathini <hbathini@linux.ibm.com> Cc: Ian Rogers <irogers@google.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Madhavan Srinivasan <maddy@linux.ibm.com> Cc: Segher Boessenkool <segher@kernel.crashing.org> Link: https://lore.kernel.org/lkml/20240718084358.72242-5-atrajeev@linux.vnet.ibm.com [ Add check for strndup() result ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
241 lines
4.6 KiB
C
241 lines
4.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/tools/lib/string.c
|
|
*
|
|
* Copied from linux/lib/string.c, where it is:
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
*
|
|
* More specifically, the first copied function was strtobool, which
|
|
* was introduced by:
|
|
*
|
|
* d0f1fed29e6e ("Add a strtobool function matching semantics of existing in kernel equivalents")
|
|
* Author: Jonathan Cameron <jic23@cam.ac.uk>
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/compiler.h>
|
|
|
|
/**
|
|
* memdup - duplicate region of memory
|
|
*
|
|
* @src: memory region to duplicate
|
|
* @len: memory region length
|
|
*/
|
|
void *memdup(const void *src, size_t len)
|
|
{
|
|
void *p = malloc(len);
|
|
|
|
if (p)
|
|
memcpy(p, src, len);
|
|
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* strtobool - convert common user inputs into boolean values
|
|
* @s: input string
|
|
* @res: result
|
|
*
|
|
* This routine returns 0 iff the first character is one of 'Yy1Nn0', or
|
|
* [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value
|
|
* pointed to by res is updated upon finding a match.
|
|
*/
|
|
int strtobool(const char *s, bool *res)
|
|
{
|
|
if (!s)
|
|
return -EINVAL;
|
|
|
|
switch (s[0]) {
|
|
case 'y':
|
|
case 'Y':
|
|
case '1':
|
|
*res = true;
|
|
return 0;
|
|
case 'n':
|
|
case 'N':
|
|
case '0':
|
|
*res = false;
|
|
return 0;
|
|
case 'o':
|
|
case 'O':
|
|
switch (s[1]) {
|
|
case 'n':
|
|
case 'N':
|
|
*res = true;
|
|
return 0;
|
|
case 'f':
|
|
case 'F':
|
|
*res = false;
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/**
|
|
* strlcpy - Copy a C-string into a sized buffer
|
|
* @dest: Where to copy the string to
|
|
* @src: Where to copy the string from
|
|
* @size: size of destination buffer
|
|
*
|
|
* Compatible with *BSD: the result is always a valid
|
|
* NUL-terminated string that fits in the buffer (unless,
|
|
* of course, the buffer size is zero). It does not pad
|
|
* out the result like strncpy() does.
|
|
*
|
|
* If libc has strlcpy() then that version will override this
|
|
* implementation:
|
|
*/
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wignored-attributes"
|
|
#endif
|
|
size_t __weak strlcpy(char *dest, const char *src, size_t size)
|
|
{
|
|
size_t ret = strlen(src);
|
|
|
|
if (size) {
|
|
size_t len = (ret >= size) ? size - 1 : ret;
|
|
memcpy(dest, src, len);
|
|
dest[len] = '\0';
|
|
}
|
|
return ret;
|
|
}
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
/**
|
|
* skip_spaces - Removes leading whitespace from @str.
|
|
* @str: The string to be stripped.
|
|
*
|
|
* Returns a pointer to the first non-whitespace character in @str.
|
|
*/
|
|
char *skip_spaces(const char *str)
|
|
{
|
|
while (isspace(*str))
|
|
++str;
|
|
return (char *)str;
|
|
}
|
|
|
|
/**
|
|
* strim - Removes leading and trailing whitespace from @s.
|
|
* @s: The string to be stripped.
|
|
*
|
|
* Note that the first trailing whitespace is replaced with a %NUL-terminator
|
|
* in the given string @s. Returns a pointer to the first non-whitespace
|
|
* character in @s.
|
|
*/
|
|
char *strim(char *s)
|
|
{
|
|
size_t size;
|
|
char *end;
|
|
|
|
size = strlen(s);
|
|
if (!size)
|
|
return s;
|
|
|
|
end = s + size - 1;
|
|
while (end >= s && isspace(*end))
|
|
end--;
|
|
*(end + 1) = '\0';
|
|
|
|
return skip_spaces(s);
|
|
}
|
|
|
|
/*
|
|
* remove_spaces - Removes whitespaces from @s
|
|
*/
|
|
void remove_spaces(char *s)
|
|
{
|
|
char *d = s;
|
|
|
|
do {
|
|
while (*d == ' ')
|
|
++d;
|
|
} while ((*s++ = *d++));
|
|
}
|
|
|
|
/**
|
|
* strreplace - Replace all occurrences of character in string.
|
|
* @s: The string to operate on.
|
|
* @old: The character being replaced.
|
|
* @new: The character @old is replaced with.
|
|
*
|
|
* Returns pointer to the nul byte at the end of @s.
|
|
*/
|
|
char *strreplace(char *s, char old, char new)
|
|
{
|
|
for (; *s; ++s)
|
|
if (*s == old)
|
|
*s = new;
|
|
return s;
|
|
}
|
|
|
|
static void *check_bytes8(const u8 *start, u8 value, unsigned int bytes)
|
|
{
|
|
while (bytes) {
|
|
if (*start != value)
|
|
return (void *)start;
|
|
start++;
|
|
bytes--;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* memchr_inv - Find an unmatching character in an area of memory.
|
|
* @start: The memory area
|
|
* @c: Find a character other than c
|
|
* @bytes: The size of the area.
|
|
*
|
|
* returns the address of the first character other than @c, or %NULL
|
|
* if the whole buffer contains just @c.
|
|
*/
|
|
void *memchr_inv(const void *start, int c, size_t bytes)
|
|
{
|
|
u8 value = c;
|
|
u64 value64;
|
|
unsigned int words, prefix;
|
|
|
|
if (bytes <= 16)
|
|
return check_bytes8(start, value, bytes);
|
|
|
|
value64 = value;
|
|
value64 |= value64 << 8;
|
|
value64 |= value64 << 16;
|
|
value64 |= value64 << 32;
|
|
|
|
prefix = (unsigned long)start % 8;
|
|
if (prefix) {
|
|
u8 *r;
|
|
|
|
prefix = 8 - prefix;
|
|
r = check_bytes8(start, value, prefix);
|
|
if (r)
|
|
return r;
|
|
start += prefix;
|
|
bytes -= prefix;
|
|
}
|
|
|
|
words = bytes / 8;
|
|
|
|
while (words) {
|
|
if (*(u64 *)start != value64)
|
|
return check_bytes8(start, value, 8);
|
|
start += 8;
|
|
words--;
|
|
}
|
|
|
|
return check_bytes8(start, value, bytes % 8);
|
|
}
|