diff --git a/src/applications/likwid-sysfeatures.lua b/src/applications/likwid-sysfeatures.lua index a8b98641d..1627c4e07 100644 --- a/src/applications/likwid-sysfeatures.lua +++ b/src/applications/likwid-sysfeatures.lua @@ -52,8 +52,8 @@ function usage() print_stdout("-h, --help\t\t Help message") print_stdout("-v, --version\t\t Version information") print_stdout("-a, --all\t\t List all available features") - print_stdout("-l, --list\t\t List features and state for given hardware threads") - print_stdout("-c, --cpus \t Perform operations on given hardware threads") + print_stdout("-l, --list\t\t List features and state for given devices") + print_stdout("-d, --devices \t Perform operations on given devices") print_stdout("-g, --get \t Get the value of the given feature(s)") print_stdout(" \t feature format: . or just if unique") print_stdout(" \t can be a list of features") @@ -72,8 +72,7 @@ end -- main variables with defaults local listFeatures = false local allFeatures = false -local num_hwts = 0 -local hwtlist = {} +local devList = {} local getList = {} local setList = {} local cpuinfo = likwid.getCpuInfo() @@ -83,7 +82,7 @@ local verbose = 0 local output_csv = false -- parse the command line -for opt,arg in likwid.getopt(arg, {"h","v","l","c:","g:","s:","a", "O","help","version","list", "set:", "get:","all", "cpus:", "V:", "verbose:"}) do +for opt,arg in likwid.getopt(arg, {"h","v","l","d:","g:","s:","a", "O","help","version","list", "set:", "get:","all", "cpus:", "V:", "verbose:"}) do if (type(arg) == "string") then local s,e = arg:find("-"); if s == 1 then @@ -98,8 +97,8 @@ for opt,arg in likwid.getopt(arg, {"h","v","l","c:","g:","s:","a", "O","help","v elseif opt == "v" or opt == "version" then version() os.exit(0) - elseif opt == "c" or opt == "cpus"then - num_hwts, hwtlist = likwid.cpustr_to_cpulist(arg) + elseif opt == "d" or opt == "devices"then + devList = likwid.createDevicesFromString(arg) elseif opt == "l" or opt == "list" then listFeatures = true elseif opt == "O" then @@ -131,15 +130,15 @@ if (listFeatures or allFeatures) and (#getList > 0 or #setList > 0) then print_stderr("Cannot list features and get/set at the same time") os.exit(1) end -if #hwtlist == 0 then +if #devList == 0 then if listFeatures then - print_stderr("HWThread selection (-c) required for listing the state of all features") + print_stderr("Device selection (-d) required for listing the state of all features") os.exit(1) elseif #getList > 0 then - print_stderr("HWThread selection (-c) required for getting the state of given features") + print_stderr("Device selection (-d) required for getting the state of given features") os.exit(1) elseif #setList > 0 then - print_stderr("HWThread selection (-c) required for setting the state of given features") + print_stderr("Device selection (-d) required for setting the state of given features") os.exit(1) end end @@ -163,7 +162,7 @@ if err < 0 then end -- get a list of all features for the system -local list = likwid.sysFeatures_list() +local ft_list = likwid.sysFeatures_list() -- print the list if allFeatures then @@ -174,7 +173,7 @@ if allFeatures then local descs = {} local cats = {} -- create a table of categories for sorting - for _,f in pairs(list) do + for _,f in pairs(ft_list) do found = false for _, c in pairs(cats) do if c == f.Category then @@ -194,7 +193,7 @@ if allFeatures then table.insert(access, "Access") table.insert(descs, "Description") for _,c in pairs(cats) do - for _,f in pairs(list) do + for _,f in pairs(ft_list) do if f.Category == c then table.insert(names, string.format("%s.%s", f.Category, f.Name)) table.insert(types, f.Type) @@ -230,93 +229,23 @@ if allFeatures then os.exit(0) end - --- sysfeatures requires exact specification of devices --- some exist per hw thread others only for the whole node --- or the socket --- this creates all devices based on the given hw threads -local deviceTree = {} - --- there is only a single entry for the whole node --- the first entry in the list of hw threads is responsible -deviceTree[likwid.node] = {} -table.insert(deviceTree[likwid.node], {device = likwid.createDevice(likwid.node, hwtlist[1]), id = hwtlist[1]}) --- create the devices for all hw threads in the list -deviceTree[likwid.hwthread] = {} -for i, c in pairs(hwtlist) do - table.insert(deviceTree[likwid.hwthread], {device = likwid.createDevice(likwid.hwthread, c), id = c}) -end --- create the devices for all sockets which are covered by the list of given hw threads -deviceTree[likwid.socket] = {} -for sid=0, cputopo.numSockets do - local sdone = false - for _, c in pairs(hwtlist) do - for _, t in pairs(cputopo.threadPool) do - if t.apicId == c and t.packageId == sid then - table.insert(deviceTree[likwid.socket], {device = likwid.createDevice(likwid.socket, sid), id = c}) - sdone = true - break - end - end - if sdone then - break - end - end -end - --- I don't know what I'm doing but I'm trying to populate the device tree according to cores -deviceTree[likwid.core] = {} -coresAdded = {} -for _, c in pairs(hwtlist) do - for _, t in pairs(cputopo.threadPool) do - if t.apicId == c and not (coresAdded[t.coreId] ~= Nil) then - table.insert(deviceTree[likwid.core], {device = likwid.createDevice(likwid.core, t.coreId), id = c}) - coresAdded[t.coreId] = true - end - end -end - -local function getDevice(level, id) - if deviceTree[level] then - for _, entry in pairs(deviceTree[level]) do - if entry.id == id then - return entry.device - end - end - end - return nil -end - ---[[deviceTree[likwid.core] = {} -for cid=0, tonumber(cputopo.numCores) do - for _, c in pairs(hwtlist) do - for _, t in pairs(cputopo.threadPool) do - if t.apicId == c and t.coreId == cid then - table.insert(deviceTree[likwid.core], {device = likwid.createDevice(likwid.core, cid), id = c}) - end - end - end -end]] - - -if listFeatures and #hwtlist > 0 then +if listFeatures and #devList > 0 then -- prepare output table local all = {} -- first column contains the feature category and name local first = {} - table.insert(first, "Feature/HWT") - for _,f in pairs(list) do + table.insert(first, "Feature/Dev") + for _,f in pairs(ft_list) do table.insert(first, string.format("%s.%s", f.Category, f.Name)) end setmetatable(first, {align = "left"}) table.insert(all, first) - -- create one column per given hw thread with the current value of the feature - for i, c in pairs(hwtlist) do + -- create one column per given device with the current value of the feature + for i, dev in pairs(devList) do local tab = {} - table.insert(tab, string.format("HWThread %d", c)) - for _,f in pairs(list) do - local dev = getDevice(f.TypeID, c) - if dev then + table.insert(tab, string.format("%s %s", dev:typeName(), dev:id())) + for _,f in pairs(ft_list) do + if dev:typeId() == f.TypeID then if f.WriteOnly then table.insert(tab, "(wronly)") else @@ -346,13 +275,13 @@ if listFeatures and #hwtlist > 0 then os.exit(0) end -if #getList > 0 and #hwtlist > 0 then +if #getList > 0 and #devList > 0 then -- filter the full feature list to have only the selected ones -- the new list entry contains all entries of the original feature entry plus -- the user-given value local featList = {} for _, f in pairs(getList) do - for _, l in pairs(list) do + for _, l in pairs(ft_list) do if f == l.Name or f == string.format("%s.%s", l.Category, l.Name) then table.insert(featList, { TypeID = l.TypeID, @@ -367,7 +296,7 @@ if #getList > 0 and #hwtlist > 0 then end -- get all features in the new list - for i, c in pairs(hwtlist) do + for i, c in pairs(devList) do local tab = {} for _,f in pairs(featList) do -- get device from the device tree @@ -387,7 +316,7 @@ if #getList > 0 and #hwtlist > 0 then os.exit(0) end -if #setList > 0 and #hwtlist > 0 then +if #setList > 0 and #devList > 0 then -- filter the full feature list to have only the selected ones -- the new list entry contains all entries of the original feature entry plus -- the user-given value @@ -395,7 +324,7 @@ if #setList > 0 and #hwtlist > 0 then for _, f in pairs(setList) do local t = likwid.stringsplit(f, "=") if #t == 2 then - for _, l in pairs(list) do + for _, l in pairs(ft_list) do if t[1] == l.Name or t[1] == string.format("%s.%s", l.Category, l.Name) then table.insert(featList, { TypeID = l.TypeID, @@ -414,7 +343,7 @@ if #setList > 0 and #hwtlist > 0 then end -- set all features in the new list - for i, c in pairs(hwtlist) do + for i, c in pairs(devList) do local tab = {} for _,f in pairs(featList) do -- get device from the device tree @@ -435,73 +364,5 @@ if #setList > 0 and #hwtlist > 0 then os.exit(0) end - ---[[if (not (listFeatures or allFeatures)) or #enableList > 0 or #disableList > 0 then - -- Check whether there are similar entries in enable and distable list and remove them (first add to skip list, then remove from tables) - if #enableList > 0 and #disableList > 0 then - local skipList = {} - for i,e in pairs(enableList) do - for j, d in pairs(disableList) do - if (e == d) then - print_stderr(string.format("Feature %s is in enable and disable list, doing nothing for feature", e)) - table.insert(skipList, e) - end - end - end - for i, s in pairs(skipList) do - for j, e in pairs(enableList) do - if (s == e) then table.remove(enableList, j) end - end - for j, e in pairs(disableList) do - if (s == e) then table.remove(disableList, j) end - end - end - end - - -- Filter enable and disable lists to contain only valid and writable features - local realEnableList = {} - local realDisableList = {} - for _, f in pairs(list) do - for _, e in pairs(enableList) do - if f.Name == e and not f.ReadOnly then - table.insert(realEnableList, f.Name) - end - end - for _, e in pairs(disableList) do - if f.Name == e and not f.ReadOnly then - table.insert(realDisableList, f.Name) - end - end - end - - -- First enable all features for all selected hardware threads - if #realEnableList > 0 then - for i, c in pairs(hwtlist) do - local dev = likwid.createDevice(likwid.hwthread, c) - for j, f in pairs(realEnableList) do - local ret = likwid.sysFeatures_set(f, dev, 1) - if ret == true then - print_stdout(string.format("Enabled %s for HWThread %d", f, c)) - else - print_stdout(string.format("Failed %s for HWThread %d", f, c)) - end - end - end - end - -- Next disable all features for all selected hardware threads - if #realDisableList > 0 then - for i, c in pairs(hwtlist) do - local dev = likwid.createDevice(likwid.hwthread, c) - for j, f in pairs(realDisableList) do - local ret = likwid.sysFeatures_set(f, dev, 0) - if ret == true then - print_stdout(string.format("Disabled %s for HWThread %d", f, c)) - else - print_stdout(string.format("Failed %s for HWThread %d", f, c)) - end - end - end - end -end]] likwid.finalizeSysFeatures() os.exit(0) diff --git a/src/applications/likwid.lua b/src/applications/likwid.lua index 51bc7b0cf..638d32d40 100644 --- a/src/applications/likwid.lua +++ b/src/applications/likwid.lua @@ -187,6 +187,7 @@ likwid.sysFeatures_set = likwid_sysFeatures_set likwid.finalizeSysFeatures = likwid_finalizeSysFeatures likwid.createDevice = likwid_createDevice +likwid.createDevicesFromString = likwid_createDevicesFromString likwid.getCudaTopology = likwid_getCudaTopology likwid.putCudaTopology = likwid_putCudaTopology diff --git a/src/devstring.c b/src/devstring.c new file mode 100644 index 000000000..ae91c9808 --- /dev/null +++ b/src/devstring.c @@ -0,0 +1,356 @@ +#include + +#include +#include +#include + +#include + +static int btoi(const bstring bstr, int *i) +{ + const char *cstr = bdata(bstr); + char *endptr; + errno = 0; + unsigned long long result = strtoull(cstr, &endptr, 0); + if (cstr == endptr || *endptr != '\0') + return -EINVAL; + if (errno != 0) + return errno; + if (result > INT_MAX) + return -ERANGE; + *i = (int)result; + return 0; +} + +typedef struct +{ + int start; // inclusive + int end; // exclusive +} range_t; + +typedef struct +{ + int count; + range_t ranges[]; +} range_list_t; + +static int range_parse(const bstring range_str, range_t *range) +{ + /* Parse a string of style '3' (single integer) or '5-7'. */ + + /* First try to parse as single integer, if that doesn't work, split + * by '-'. */ + int start, end; + int err = btoi(range_str, &start); + if (err == 0) + { + range->start = start; + range->end = start + 1; + return 0; + } + + bstring start_str = NULL; + bstring end_str = NULL; + + int sep_index = bstrchr(range_str, '-'); + if (sep_index == BSTR_ERR) + { + err = -EINVAL; + goto cleanup; + } + + start_str = bmidstr(range_str, 0, sep_index); + end_str = bmidstr(range_str, sep_index + 1, INT_MAX); + if (!start_str || !end_str) + { + err = -ENOMEM; + goto cleanup; + } + + err = btoi(start_str, &start); + if (err < 0) + goto cleanup; + + err = btoi(end_str, &end); + if (err < 0) + goto cleanup; + + range->start = start; + range->end = end + 1; + +cleanup: + bdestroy(start_str); + bdestroy(end_str); + return err; +} + +static int range_create(const bstring range_list_str, range_list_t **rlist) +{ + /* Create a list of ranges from a string like '1-4,5,6,7-20' */ + struct bstrList *slist = bsplit(range_list_str, ','); + if (!slist) + return -ENOMEM; + + int err = 0; + range_list_t *new_rlist = malloc(sizeof(range_list_t) + sizeof(range_t) * slist->qty); + if (!new_rlist) + { + err = -ENOMEM; + goto cleanup; + } + + new_rlist->count = slist->qty; + for (int i = 0; i < slist->qty; i++) + { + err = range_parse(slist->entry[i], &new_rlist->ranges[i]); + if (err < 0) + goto cleanup; + } + + *rlist = new_rlist; + +cleanup: + bstrListDestroy(slist); + return err; +} + +static void range_destroy(range_list_t *rlist) +{ + if (!rlist) + return; + free(rlist); +} + +static int misc_init(void) +{ + int err = topology_init(); + if (err < 0) + return err; + +#ifdef LIKWID_WITH_NVMON + /* The topology init functions currently return both positive and negative + * error numbers :-/, so use this workaround for now. */ + if (topology_cuda_init() != 0) + return -EPERM; +#endif +#ifdef LIKWID_WITH_ROCMON + if (topology_rocm_init() != 0) + return -EPERM; +#endif + if (affinity_init()) + return -EPERM; + + return 0; +} + +static int device_create_from_string_and_append(LikwidDeviceType type, const char *id, LikwidDeviceList_t dev_list) +{ + LikwidDevice_t dev; + int err = likwid_device_create_from_string(type, id, &dev); + if (err < 0) + return err; + + const int new_num_devices = dev_list->num_devices + 1; + LikwidDevice_t *new_devices = realloc(dev_list->devices, new_num_devices * sizeof(dev_list->devices[0])); + if (!new_devices) + { + likwid_device_destroy(dev); + return -ENOMEM; + } + + dev_list->num_devices = new_num_devices; + dev_list->devices = new_devices; + dev_list->devices[new_num_devices - 1] = dev; + return 0; +} + +static int device_create_and_append(LikwidDeviceType type, int id, LikwidDeviceList_t dev_list) +{ + LikwidDevice_t dev; + int err = likwid_device_create(type, id, &dev); + if (err < 0) + return err; + + const int new_num_devices = dev_list->num_devices + 1; + LikwidDevice_t *new_devices = realloc(dev_list->devices, new_num_devices * sizeof(dev_list->devices[0])); + if (!new_devices) + { + likwid_device_destroy(dev); + return -ENOMEM; + } + + dev_list->num_devices = new_num_devices; + dev_list->devices = new_devices; + dev_list->devices[new_num_devices - 1] = dev; + return 0; +} + +static int parse_node(const bstring domain_selector, LikwidDeviceList_t dev_list) +{ + /* A node string must not have a domain_selector, as only domain 0 is allowed. + * Optionally domain 0 is allowed. */ + const char *ds = bdata(domain_selector); + if (domain_selector && strcmp(ds, "0") != 0) + { + ERROR_PRINT(If specified node domain may only sppecify node '0' found: '%s', bdata(domain_selector)); + return -EINVAL; + } + + return device_create_and_append(DEVICE_TYPE_NODE, 0, dev_list); +} + +static int parse_simple(const bstring domain_selector, LikwidDeviceType type, LikwidDeviceList_t dev_list) +{ + /* All others (except GPUs) have a domain_selector like this: '0,1,4-5,7' */ + range_list_t *range_list; + int err = range_create(domain_selector, &range_list); + if (err < 0) + { + ERROR_PRINT(Unable to parse malformed range: %s, bdata(domain_selector)); + return err; + } + + for (int range_index = 0; range_index < range_list->count; range_index++) + { + range_t *range = &range_list->ranges[range_index]; + for (int i = range->start; i < range->end; i++) + { + err = device_create_and_append(type, i, dev_list); + if (err < 0) + goto cleanup; + } + } + +cleanup: + range_destroy(range_list); + return err; +} + +static int parse_gpu(const bstring domain_selector, LikwidDeviceType type, LikwidDeviceList_t dev_list) +{ + /* GPUs differ once again in their domain_selector, as they also allow a list of PCI adresses. + * So it may look like '0,1,4-5,7' or '00000000:e0:1f:1,00000000:30:13:0'. */ + range_list_t *range_list; + int err = range_create(domain_selector, &range_list); + if (err == 0) + { + for (int range_index = 0; range_index < range_list->count; range_index++) + { + range_t *range = &range_list->ranges[range_index]; + for (int i = range->start; i < range->end; i++) + { + err = device_create_and_append(type, i, dev_list); + if (err < 0) + goto cleanup; + } + } + +cleanup: + range_destroy(range_list); + return err; + } + + /* If the previous if wasn't entered, check for the PCI address format here. */ + + struct bstrList *slist = bsplit(domain_selector, ','); + if (!slist) + return -ENOMEM; + + for (int i = 0; i < slist->qty; i++) + { + err = device_create_from_string_and_append(type, bdata(slist->entry[i]), dev_list); + if (err < 0) + break; + } + + bstrListDestroy(slist); + return err; +} + +static int process_domain(const bstring domain_type, const bstring domain_selector, LikwidDeviceList_t dev_list) +{ + const char *dt = bdata(domain_type); + if (strcmp(dt, "N") == 0) + return parse_node(domain_selector, dev_list); + else if (strcmp(dt, "S") == 0) + return parse_simple(domain_selector, DEVICE_TYPE_SOCKET, dev_list); + else if (strcmp(dt, "C") == 0) + return parse_simple(domain_selector, DEVICE_TYPE_CORE, dev_list); + else if (strcmp(dt, "M") == 0) + return parse_simple(domain_selector, DEVICE_TYPE_NUMA, dev_list); + else if (strcmp(dt, "D") == 0) + return parse_simple(domain_selector, DEVICE_TYPE_DIE, dev_list); +#ifdef LIKWID_WITH_NVMON + else if (strcmp(dt, "GN") == 0) + return parse_gpu(domain_selector, DEVICE_TYPE_NVIDIA_GPU, dev_list); +#endif +#ifdef LIKWID_WITH_ROCMON + else if (strcmp(dt, "GA") == 0) + return parse_gpu(domain_selector, DEVICE_TYPE_AMD_GPU, dev_list); +#endif + ERROR_PRINT(Unknown domain type: '%s', dt); + return -EINVAL; +} + +static int parse_domain(const bstring dev_bstr, LikwidDeviceList_t dev_list) +{ + /* Parse a domain name like 'M:0-4,7' to 'M' and '0-4,7'. + * We call this the domain_type and domain_selector. */ + int err; + int sep_index = bstrchr(dev_bstr, ':'); + if (sep_index == BSTR_ERR) + { + err = process_domain(dev_bstr, NULL, dev_list); + } + else + { + bstring domain_name = bmidstr(dev_bstr, 0, sep_index); + if (!domain_name) + return -ENOMEM; + bstring domain_selector = bmidstr(dev_bstr, sep_index + 1, INT_MAX); + if (!domain_selector) + { + bdestroy(domain_name); + return -ENOMEM; + } + err = process_domain(domain_name, domain_selector, dev_list); + bdestroy(domain_selector); + bdestroy(domain_name); + } + return err; +} + +int likwid_devstr_to_devlist(const char *str, LikwidDeviceList_t *dev_list) +{ + /* Init */ + int err = misc_init(); + if (err < 0) + return err; + + /* Alloc list */ + LikwidDeviceList_t new_list = calloc(1, sizeof(_LikwidDeviceList)); + if (!new_list) + return -ENOMEM; + + /* Split input string by '@' */ + bstring bstr = bfromcstr(str); + struct bstrList *bstr_list = bsplit(bstr, '@'); + + if (!bstr_list) + { + free(new_list); + return -ENOMEM; + } + + /* Iterate over all devices and add them to the list */ + for (int i = 0; i < bstr_list->qty; i++) + { + err = parse_domain(bstr_list->entry[i], new_list); + if (err < 0) + break; + } + + *dev_list = new_list; + bstrListDestroy(bstr_list); + return err; +} diff --git a/src/includes/devstring.h b/src/includes/devstring.h new file mode 100644 index 000000000..052e21747 --- /dev/null +++ b/src/includes/devstring.h @@ -0,0 +1,10 @@ +#ifndef DEVSTRING_H +#define DEVSTRING_H + +#include + +#include + +int likwid_devstr_to_devlist(const char *str, LikwidDeviceList_t *dev_list); + +#endif // DEVSTRING_H diff --git a/src/includes/likwid.h b/src/includes/likwid.h index 1f0667252..146a226ae 100644 --- a/src/includes/likwid.h +++ b/src/includes/likwid.h @@ -3253,7 +3253,7 @@ typedef _LikwidDevice* LikwidDevice_t; typedef struct { int num_devices; - LikwidDevice_t devices; + LikwidDevice_t *devices; } _LikwidDeviceList; typedef _LikwidDeviceList* LikwidDeviceList_t; @@ -3274,10 +3274,26 @@ static char* LikwidDeviceTypeNames[MAX_DEVICE_TYPE] = { #endif }; +/* \brief Read LikwidDevice selection string and create specified devices. + +Read the LikwidDevice selection string and creates list with created devices. +The format is 'type:id_list'. 'type' is N (node), S (socket), C (chip), +M (NUMA node), D (die), GN (Nvidia GPU), GA (AMD GPU). N does not have id_list, +since there is always one node only. 'id_list' is a comma separated list of +integers or integer ranges specified as '2-15' (inclusive bounds). +GPUs may also use PCI adresses (e.g. 00000000:20:1f:0) instead of integer numbers. +@param [in] str LikwidDevice selection string +@param [out] dev_list Created device list (in case of success). +@return error code (<0 on failure) +*/ +int likwid_devstr_to_devlist(const char *str, LikwidDeviceList_t *dev_list) + __attribute__((visibility("default"))); + int likwid_device_create(LikwidDeviceType type, int id, LikwidDevice_t* device) __attribute__ ((visibility ("default") )); int likwid_device_create_from_string(LikwidDeviceType type, const char *id, LikwidDevice_t* device) __attribute__ ((visibility ("default") )); void likwid_device_destroy(LikwidDevice_t device) __attribute__ ((visibility ("default") )); -char* device_type_name(LikwidDeviceType type) __attribute__ ((visibility ("default") )); +const char *likwid_device_type_name(LikwidDeviceType type) __attribute__ ((visibility ("default") )); +void likwid_device_fmt_pci(char *buf, size_t size, LikwidDevice_t device) __attribute__ ((visibility ("default") )); typedef struct { char* name; diff --git a/src/likwid_device.c b/src/likwid_device.c index 5ba6dc15b..3794427f2 100644 --- a/src/likwid_device.c +++ b/src/likwid_device.c @@ -456,11 +456,18 @@ void likwid_device_destroy(LikwidDevice_t device) } } -char* device_type_name(LikwidDeviceType type) +const char *likwid_device_type_name(LikwidDeviceType type) { - if ((type < DEVICE_TYPE_INVALID) || (type >= MAX_DEVICE_TYPE)) - { + if (type < DEVICE_TYPE_INVALID || type >= MAX_DEVICE_TYPE) return "unsupported"; - } return LikwidDeviceTypeNames[type]; } + +void likwid_device_fmt_pci(char *buf, size_t size, LikwidDevice_t device) +{ + const uint16_t dom = device->id.pci.pci_domain; + const uint8_t bus = device->id.pci.pci_bus; + const uint8_t dev = device->id.pci.pci_dev; + const uint8_t func = device->id.pci.pci_func; + snprintf(buf, size, "%08x:%02x:%02x.%01x", dom, bus, dev, func); +} diff --git a/src/luawid.c b/src/luawid.c index dbb189006..9d1582831 100644 --- a/src/luawid.c +++ b/src/luawid.c @@ -1247,33 +1247,29 @@ static int lua_likwid_getAffinityInfo(lua_State *L) { static int lua_likwid_cpustr_to_cpulist(lua_State* L) { - int ret = 0; - char* cpustr = (char *)luaL_checkstring(L, 1); + const char *cpustr = luaL_checkstring(L, 1); if (!cputopo) { topology_init(); cputopo = get_cpuTopology(); topology_isInitialized = 1; } - int* cpulist = (int*) malloc(cputopo->numHWThreads * sizeof(int)); + int *cpulist = malloc(cputopo->numHWThreads * sizeof(int)); if (cpulist == NULL) - { - lua_pushnumber(L, 0); - return 1; - } - ret = cpustr_to_cpulist(cpustr, cpulist, cputopo->numHWThreads); - if (ret <= 0) + return luaL_error(L, "Cannot allocate memory"); + + int listlen = cpustr_to_cpulist(cpustr, cpulist, cputopo->numHWThreads); + if (listlen < 0) { free(cpulist); - lua_pushnumber(L, 0); - return 1; + return luaL_error(L, "cpustr_to_cpulist() failed (%d): %s", listlen, strerror(listlen)); } - lua_pushnumber(L, ret); + lua_pushnumber(L, listlen); lua_newtable(L); - for (int i=0;itype); + return 1; +} + +static int lua_likwiddevice_get_typeName(lua_State *L) +{ + const LikwidDevice_t dev = *(LikwidDevice_t *)luaL_checkudata(L, 1, "LikwidDevice_t"); + lua_pushstring(L, likwid_device_type_name(dev->type)); + return 1; +} + +static int lua_likwiddevice_get_id(lua_State *L) +{ + const LikwidDevice_t dev = *(LikwidDevice_t *)luaL_checkudata(L, 1, "LikwidDevice_t"); + + char buf[128]; +#ifdef LIKWID_WITH_NVMON + if (dev->type == DEVICE_TYPE_NVIDIA_GPU) + { + likwid_device_fmt_pci(buf, sizeof(buf), dev); + lua_pushstring(L, buf); + return 1; + } +#endif +#ifdef LIKWID_WITH_ROCMON + if (dev->type == DEVICE_TYPE_AMD_GPU) + { + likwid_device_fmt_pci(buf, sizeof(buf), dev); + lua_pushstring(L, buf); + return 1; + } +#endif + snprintf(buf, sizeof(buf), "%d", dev->id); + lua_pushstring(L, buf); + return 1; +} + +static int lua_likwiddevice_get_internalId(lua_State *L) +{ + const LikwidDevice_t dev = *(LikwidDevice_t *)luaL_checkudata(L, 1, "LikwidDevice_t"); + lua_pushinteger(L, dev->internal_id); + return 1; +} + +static void push_likwiddevice(lua_State *L, LikwidDevice_t dev) +{ + LikwidDevice_t *luadev = lua_newuserdata(L, sizeof(luadev)); + *luadev = dev; + + luaL_newmetatable(L, "LikwidDevice_t"); + + /* Create deleter */ + lua_pushcfunction(L, lua_likwid_destroyDevice); + lua_setfield(L, -2, "__gc"); + + /* Create index for members */ + lua_newtable(L); + + lua_pushstring(L, "typeId"); + lua_pushcfunction(L, lua_likwiddevice_get_typeId); + lua_settable(L, -3); + + lua_pushstring(L, "typeName"); + lua_pushcfunction(L, lua_likwiddevice_get_typeName); + lua_settable(L, -3); + + lua_pushstring(L, "id"); + lua_pushcfunction(L, lua_likwiddevice_get_id); + lua_settable(L, -3); + + lua_pushstring(L, "internalId"); + lua_pushcfunction(L, lua_likwiddevice_get_internalId); + lua_settable(L, -3); + + /* Set the created table as index for members. */ + lua_setfield(L, -2, "__index"); + + /* Last, assign the metatable to the likwid device. */ + lua_setmetatable(L, -2); +} + +static int +lua_likwid_createDevicesFromString(lua_State *L) +{ + const char *devstr = luaL_checkstring(L, 1); + + LikwidDeviceList_t dev_list; + int err = likwid_devstr_to_devlist(devstr, &dev_list); + if (err < 0) + luaL_error(L, "Cannot create device list: %s", strerror(-err)); + + lua_newtable(L); + for (int i = 0; i < dev_list->num_devices; i++) + { + lua_pushinteger(L, i + 1); + push_likwiddevice(L, dev_list->devices[i]); + lua_settable(L, -3); + } + + /* Do not delete the devices themselves (only the pointer array + list object), + * since the devices themselves are returned in the lua list. */ + free(dev_list->devices); + free(dev_list); + return 1; +} + static int lua_likwid_createDevice(lua_State *L) { @@ -3855,16 +3960,7 @@ lua_likwid_createDevice(lua_State *L) /* what happens with 'dev' if any Lua functions fail? We probably leak memory, * but I don't know how to avoid that... */ - LikwidDevice_t *luadev = lua_newuserdata(L, sizeof(dev)); - *luadev = dev; - - luaL_newmetatable(L, "LikwidDevice_Finalizer"); - { - lua_pushcfunction(L, lua_likwid_destroyDevice); - lua_setfield(L, -2, "__gc"); - } - - lua_setmetatable(L, -2); + push_likwiddevice(L, dev); return 1; } #endif /* LIKWID_WITH_SYSFEATURES */ @@ -4121,6 +4217,7 @@ int __attribute__((visibility("default"))) luaopen_liblikwid(lua_State *L) { lua_register(L, "likwid_sysFeatures_list",lua_likwid_getSysFeatureList); lua_register(L, "likwid_sysFeatures_get",lua_likwid_getSysFeature); lua_register(L, "likwid_sysFeatures_set",lua_likwid_setSysFeature); + lua_register(L, "likwid_createDevicesFromString",lua_likwid_createDevicesFromString); lua_register(L, "likwid_createDevice",lua_likwid_createDevice); #endif /* LIKWID_WITH_SYSFEATURES */ #ifdef __MIC__