Clock, Power and Performance Queries

Clock, Power and Performance Queries#

AMD SMI: Clock, Power and Performance Queries
Clock, Power and Performance Queries

Functions

amdsmi_status_t amdsmi_get_utilization_count (amdsmi_processor_handle processor_handle, amdsmi_utilization_counter_t utilization_counters[], uint32_t count, uint64_t *timestamp)
 Get coarse grain utilization counter of the specified device. More...
 
amdsmi_status_t amdsmi_get_gpu_perf_level (amdsmi_processor_handle processor_handle, amdsmi_dev_perf_level_t *perf)
 Get the performance level of the device. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_set_gpu_perf_determinism_mode (amdsmi_processor_handle processor_handle, uint64_t clkvalue)
 Enter performance determinism mode with provided processor handle. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_overdrive_level (amdsmi_processor_handle processor_handle, uint32_t *od)
 Get the overdrive percent associated with the device with provided processor handle. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_mem_overdrive_level (amdsmi_processor_handle processor_handle, uint32_t *od)
 Get the GPU memory clock overdrive percent associated with the device with provided processor handle. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_clk_freq (amdsmi_processor_handle processor_handle, amdsmi_clk_type_t clk_type, amdsmi_frequencies_t *f)
 Get the list of possible system clock speeds of device for a specified clock type. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_reset_gpu (amdsmi_processor_handle processor_handle)
 Reset the gpu associated with the device with provided processor handle. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_od_volt_info (amdsmi_processor_handle processor_handle, amdsmi_od_volt_freq_data_t *odv)
 This function retrieves the overdrive GFX & MCLK information. If valid for the GPU it will also populate the voltage curve data. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_metrics_header_info (amdsmi_processor_handle processor_handle, amd_metrics_table_header_t *header_value)
 Get the 'metrics_header_info' from the GPU metrics associated with the device. More...
 
amdsmi_status_t amdsmi_get_gpu_metrics_info (amdsmi_processor_handle processor_handle, amdsmi_gpu_metrics_t *pgpu_metrics)
 This function retrieves the gpu metrics information. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_pm_metrics_info (amdsmi_processor_handle processor_handle, amdsmi_name_value_t **pm_metrics, uint32_t *num_of_metrics)
 Get the pm metrics table with provided device index. More...
 
amdsmi_status_t amdsmi_get_gpu_reg_table_info (amdsmi_processor_handle processor_handle, amdsmi_reg_type_t reg_type, amdsmi_name_value_t **reg_metrics, uint32_t *num_of_metrics)
 Get the register metrics table with provided device index and register type. More...
 
amdsmi_status_t amdsmi_set_gpu_clk_range (amdsmi_processor_handle processor_handle, uint64_t minclkvalue, uint64_t maxclkvalue, amdsmi_clk_type_t clkType)
 This function sets the clock range information. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_set_gpu_clk_limit (amdsmi_processor_handle processor_handle, amdsmi_clk_type_t clk_type, amdsmi_clk_limit_type_t limit_type, uint64_t clk_value)
 This function sets the clock sets the clock min/max level. More...
 
void amdsmi_free_name_value_pairs (void *p)
 Frees heap memory allocated by reg_table and pm_metrics. More...
 
amdsmi_status_t amdsmi_set_gpu_od_clk_info (amdsmi_processor_handle processor_handle, amdsmi_freq_ind_t level, uint64_t clkvalue, amdsmi_clk_type_t clkType)
 This function sets the clock frequency information. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_set_gpu_od_volt_info (amdsmi_processor_handle processor_handle, uint32_t vpoint, uint64_t clkvalue, uint64_t voltvalue)
 This function sets 1 of the 3 voltage curve points. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_od_volt_curve_regions (amdsmi_processor_handle processor_handle, uint32_t *num_regions, amdsmi_freq_volt_region_t *buffer)
 This function will retrieve the current valid regions in the frequency/voltage space. It is not supported on virtual machine guest. More...
 
amdsmi_status_t amdsmi_get_gpu_power_profile_presets (amdsmi_processor_handle processor_handle, uint32_t sensor_ind, amdsmi_power_profile_status_t *status)
 Get the list of available preset power profiles and an indication of which profile is currently active. It is not supported on virtual machine guest. More...
 

Detailed Description

These functions provide information about clock frequencies and performance.

Function Documentation

◆ amdsmi_get_utilization_count()

amdsmi_status_t amdsmi_get_utilization_count ( amdsmi_processor_handle  processor_handle,
amdsmi_utilization_counter_t  utilization_counters[],
uint32_t  count,
uint64_t *  timestamp 
)

Get coarse grain utilization counter of the specified device.

Platform:
gpu_bm_linux

Given a processor handle processor_handle, the array of the utilization counters, the size of the array, this function returns the coarse grain utilization counters and timestamp. The counter is the accumulated percentages. Every milliseconds the firmware calculates % busy count and then accumulates that value in the counter. This provides minimally invasive coarse grain GPU usage information.

Parameters
[in]processor_handlea processor handle
[in,out]utilization_countersMultiple utilization counters can be retreived with a single call. The caller must allocate enough space to the utilization_counters array. The caller also needs to set valid AMDSMI_UTILIZATION_COUNTER_TYPE type for each element of the array. AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.

If the function reutrns AMDSMI_STATUS_SUCCESS, the counter will be set in the value field of the amdsmi_utilization_counter_t.

Parameters
[in]countThe size of utilization_counters array.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_perf_level()

amdsmi_status_t amdsmi_get_gpu_perf_level ( amdsmi_processor_handle  processor_handle,
amdsmi_dev_perf_level_t perf 
)

Get the performance level of the device. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

This function will write the amdsmi_dev_perf_level_t to the uint32_t pointed to by perf, for a given processor handle processor_handle and a pointer to a uint32_t perf.

Parameters
[in]processor_handlea processor handle
[in,out]perfa pointer to amdsmi_dev_perf_level_t to which the performance level will be written If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_set_gpu_perf_determinism_mode()

amdsmi_status_t amdsmi_set_gpu_perf_determinism_mode ( amdsmi_processor_handle  processor_handle,
uint64_t  clkvalue 
)

Enter performance determinism mode with provided processor handle. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle and clkvalue this function will enable performance determinism mode, which enforces a GFXCLK frequency SoftMax limit per GPU set by the user. This prevents the GFXCLK PLL from stretching when running the same workload on different GPUS, making performance variation minimal. This call will result in the performance level amdsmi_dev_perf_level_t of the device being AMDSMI_DEV_PERF_LEVEL_DETERMINISM.

Parameters
[in]processor_handlea processor handle
[in]clkvalueSoftmax value for GFXCLK in MHz.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_overdrive_level()

amdsmi_status_t amdsmi_get_gpu_overdrive_level ( amdsmi_processor_handle  processor_handle,
uint32_t *  od 
)

Get the overdrive percent associated with the device with provided processor handle. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle and a pointer to a uint32_t od, this function will write the overdrive percentage to the uint32_t pointed to by od

Parameters
[in]processor_handlea processor handle
[in,out]oda pointer to uint32_t to which the overdrive percentage will be written If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_mem_overdrive_level()

amdsmi_status_t amdsmi_get_gpu_mem_overdrive_level ( amdsmi_processor_handle  processor_handle,
uint32_t *  od 
)

Get the GPU memory clock overdrive percent associated with the device with provided processor handle. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle and a pointer to a uint32_t od, this function will write the overdrive percentage to the uint32_t pointed to by od

Parameters
[in]processor_handlea processor handle
[in,out]oda pointer to uint32_t to which the GPU memory clock overdrive percentage will be written If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_clk_freq()

amdsmi_status_t amdsmi_get_clk_freq ( amdsmi_processor_handle  processor_handle,
amdsmi_clk_type_t  clk_type,
amdsmi_frequencies_t f 
)

Get the list of possible system clock speeds of device for a specified clock type. It is not supported on virtual machine guest.

Platform:

gpu_bm_linux

guest_1vf

Given a processor handle processor_handle, a clock type clk_type, and a pointer to a to an amdsmi_frequencies_t structure f, this function will fill in f with the possible clock speeds, and indication of the current clock speed selection.

Parameters
[in]processor_handlea processor handle
[in]clk_typethe type of clock for which the frequency is desired
[in,out]fa pointer to a caller provided amdsmi_frequencies_t structure to which the frequency information will be written. Frequency values are in Hz.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_reset_gpu()

amdsmi_status_t amdsmi_reset_gpu ( amdsmi_processor_handle  processor_handle)

Reset the gpu associated with the device with provided processor handle. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle, this function will reset the GPU

Parameters
[in]processor_handlea processor handle
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_od_volt_info()

amdsmi_status_t amdsmi_get_gpu_od_volt_info ( amdsmi_processor_handle  processor_handle,
amdsmi_od_volt_freq_data_t odv 
)

This function retrieves the overdrive GFX & MCLK information. If valid for the GPU it will also populate the voltage curve data. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle and a pointer to a amdsmi_od_volt_freq_data_t structure odv, this function will populate odv. See amdsmi_od_volt_freq_data_t for more details.

Parameters
[in]processor_handlea processor handle
[in,out]odva pointer to an amdsmi_od_volt_freq_data_t structure If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_metrics_header_info()

amdsmi_status_t amdsmi_get_gpu_metrics_header_info ( amdsmi_processor_handle  processor_handle,
amd_metrics_table_header_t header_value 
)

Get the 'metrics_header_info' from the GPU metrics associated with the device.

Platform:

gpu_bm_linux

guest_1vf

Given a processor handle processor_handle and a pointer to a amd_metrics_table_header_t in which the 'metrics_header_info' will stored

Parameters
[in]processor_handleDevice which to query
[in,out]header_valuea pointer to amd_metrics_table_header_t to which the device gpu metric unit will be stored
Return values
AMDSMI_STATUS_SUCCESSis returned upon successful call. AMDSMI_STATUS_NOT_SUPPORTED is returned in case the metric unit does not exist for the given device

◆ amdsmi_get_gpu_metrics_info()

amdsmi_status_t amdsmi_get_gpu_metrics_info ( amdsmi_processor_handle  processor_handle,
amdsmi_gpu_metrics_t pgpu_metrics 
)

This function retrieves the gpu metrics information. It is not supported on virtual machine guest.

Platform:

gpu_bm_linux

guest_1vf

Given a processor handle processor_handle and a pointer to a amdsmi_gpu_metrics_t structure pgpu_metrics, this function will populate pgpu_metrics. See amdsmi_gpu_metrics_t for more details.

Parameters
[in]processor_handlea processor handle
[in,out]pgpu_metricsa pointer to an amdsmi_gpu_metrics_t structure If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_pm_metrics_info()

amdsmi_status_t amdsmi_get_gpu_pm_metrics_info ( amdsmi_processor_handle  processor_handle,
amdsmi_name_value_t **  pm_metrics,
uint32_t *  num_of_metrics 
)

Get the pm metrics table with provided device index.

Platform:
gpu_bm_linux

Given a device handle processor_handle, pm_metrics pointer, and num_of_metrics pointer, this function will write the pm metrics name value pair to the array at pm_metrics and the number of metrics retreived to num_of_metrics Note: the library allocated memory for pm_metrics, and user must call free(pm_metrics) to free it after use.

Parameters
[in]processor_handlea processor handle
[in,out]pm_metricsA pointerto an array to hold multiple PM metrics. On successs, the library will allocate memory of pm_metrics and write metrics to this array. The caller must free this memory after usage to avoid memory leak.
[in,out]num_of_metricsa pointer to uint32_t to which the number of metrics is allocated for pm_metrics array as input, and the number of metrics retreived as output. If this parameter is NULL, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Return values
AMDSMI_STATUS_SUCCESScall was successful
AMDSMI_STATUS_NOT_SUPPORTEDinstalled software or hardware does not support this function with the given arguments
AMDSMI_STATUS_INVALthe provided arguments are not valid

◆ amdsmi_get_gpu_reg_table_info()

amdsmi_status_t amdsmi_get_gpu_reg_table_info ( amdsmi_processor_handle  processor_handle,
amdsmi_reg_type_t  reg_type,
amdsmi_name_value_t **  reg_metrics,
uint32_t *  num_of_metrics 
)

Get the register metrics table with provided device index and register type.

Platform:
gpu_bm_linux

Given a device handle processor_handle, reg_type, reg_metrics pointer, and num_of_metrics pointer, this function will write the register metrics name value pair to the array at reg_metrics and the number of metrics retreived to num_of_metrics Note: the library allocated memory for reg_metrics, and user must call free(reg_metrics) to free it after use.

Parameters
[in]processor_handlea processor handle
[in]reg_typeThe register type
[in,out]reg_metricsA pointerto an array to hold multiple register metrics. On successs, the library will allocate memory of reg_metrics and write metrics to this array. The caller must free this memory after usage to avoid memory leak.
[in,out]num_of_metricsa pointer to uint32_t to which the number of metrics is allocated for reg_metrics array as input, and the number of metrics retreived as output. If this parameter is NULL, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Return values
AMDSMI_STATUS_SUCCESScall was successful
AMDSMI_STATUS_NOT_SUPPORTEDinstalled software or hardware does not support this function with the given arguments
AMDSMI_STATUS_INVALthe provided arguments are not valid

◆ amdsmi_set_gpu_clk_range()

amdsmi_status_t amdsmi_set_gpu_clk_range ( amdsmi_processor_handle  processor_handle,
uint64_t  minclkvalue,
uint64_t  maxclkvalue,
amdsmi_clk_type_t  clkType 
)

This function sets the clock range information. It is not supported on virtual machine guest.

Deprecated:
amdsmi_set_gpu_clk_limit() should be used, with an interface that set the min_value and then max_value.
Platform:
gpu_bm_linux

Given a processor handle processor_handle, a minimum clock value minclkvalue, a maximum clock value maxclkvalue and a clock type clkType this function will set the sclk|mclk range

Parameters
[in]processor_handlea processor handle
[in]minclkvaluevalue to apply to the clock range. Frequency values are in MHz.
[in]maxclkvaluevalue to apply to the clock range. Frequency values are in MHz.
[in]clkTypeAMDSMI_CLK_TYPE_SYS | AMDSMI_CLK_TYPE_MEM range type
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_set_gpu_clk_limit()

amdsmi_status_t amdsmi_set_gpu_clk_limit ( amdsmi_processor_handle  processor_handle,
amdsmi_clk_type_t  clk_type,
amdsmi_clk_limit_type_t  limit_type,
uint64_t  clk_value 
)

This function sets the clock sets the clock min/max level.

Platform:

gpu_bm_linux

guest_1vf

Given a processor handle processor_handle, a clock type clk_type, a value clk_value needs to be set, and the level indicates min or max clock you want to set, this function the clock limit.

Parameters
[in]processor_handlea processor handle
[in]clk_typeAMDSMI_CLK_TYPE_SYS, AMDSMI_CLK_TYPE_MEM and so on
[in]limit_typeAMDSMI_FREQ_IND_MIN|AMDSMI_FREQ_IND_MAX to set the minimum (0) or maximum (1) speed.
[in]clk_valuevalue to apply to. Frequency values are in MHz.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_free_name_value_pairs()

void amdsmi_free_name_value_pairs ( void *  p)

Frees heap memory allocated by reg_table and pm_metrics.

Platform:
gpu_bm_linux

Frees heap memory.

Parameters
[in]pa pointer to the memory to free.

◆ amdsmi_set_gpu_od_clk_info()

amdsmi_status_t amdsmi_set_gpu_od_clk_info ( amdsmi_processor_handle  processor_handle,
amdsmi_freq_ind_t  level,
uint64_t  clkvalue,
amdsmi_clk_type_t  clkType 
)

This function sets the clock frequency information. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle, a frequency level level, a clock value clkvalue and a clock type clkType this function will set the sclk|mclk range

Parameters
[in]processor_handlea processor handle
[in]levelAMDSMI_FREQ_IND_MIN|AMDSMI_FREQ_IND_MAX to set the minimum (0) or maximum (1) speed.
[in]clkvaluevalue to apply to the clock range. Frequency values are in MHz.
[in]clkTypeAMDSMI_CLK_TYPE_SYS | AMDSMI_CLK_TYPE_MEM range type
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_set_gpu_od_volt_info()

amdsmi_status_t amdsmi_set_gpu_od_volt_info ( amdsmi_processor_handle  processor_handle,
uint32_t  vpoint,
uint64_t  clkvalue,
uint64_t  voltvalue 
)

This function sets 1 of the 3 voltage curve points. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle, a voltage point vpoint and a voltage value voltvalue this function will set voltage curve point

Parameters
[in]processor_handlea processor handle
[in]vpointvoltage point [0|1|2] on the voltage curve
[in]clkvalueclock value component of voltage curve point. Frequency values are in MHz.
[in]voltvaluevoltage value component of voltage curve point. Voltage is in mV.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_od_volt_curve_regions()

amdsmi_status_t amdsmi_get_gpu_od_volt_curve_regions ( amdsmi_processor_handle  processor_handle,
uint32_t *  num_regions,
amdsmi_freq_volt_region_t buffer 
)

This function will retrieve the current valid regions in the frequency/voltage space. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle, a pointer to an unsigned integer num_regions and a buffer of amdsmi_freq_volt_region_t structures, buffer, this function will populate buffer with the current frequency-volt space regions. The caller should assign buffer to memory that can be written to by this function. The caller should also indicate the number of amdsmi_freq_volt_region_t structures that can safely be written to buffer in num_regions.

The number of regions to expect this function provide (num_regions) can be obtained by calling :: amdsmi_get_gpu_od_volt_info().

Parameters
[in]processor_handlea processor handle
[in,out]num_regionsAs input, this is the number of amdsmi_freq_volt_region_t structures that can be written to buffer. As output, this is the number of amdsmi_freq_volt_region_t structures that were actually written. If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
[in,out]buffera caller provided buffer to which amdsmi_freq_volt_region_t structures will be written If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail

◆ amdsmi_get_gpu_power_profile_presets()

amdsmi_status_t amdsmi_get_gpu_power_profile_presets ( amdsmi_processor_handle  processor_handle,
uint32_t  sensor_ind,
amdsmi_power_profile_status_t status 
)

Get the list of available preset power profiles and an indication of which profile is currently active. It is not supported on virtual machine guest.

Platform:
gpu_bm_linux

Given a processor handle processor_handle and a pointer to a amdsmi_power_profile_status_t status, this function will set the bits of the amdsmi_power_profile_status_t.available_profiles bit field of status to 1 if the profile corresponding to the respective amdsmi_power_profile_preset_masks_t profiles are enabled. For example, if both the VIDEO and VR power profiles are available selections, then AMDSMI_PWR_PROF_PRST_VIDEO_MASK AND'ed with amdsmi_power_profile_status_t.available_profiles will be non-zero as will AMDSMI_PWR_PROF_PRST_VR_MASK AND'ed with amdsmi_power_profile_status_t.available_profiles. Additionally, amdsmi_power_profile_status_t.current will be set to the amdsmi_power_profile_preset_masks_t of the profile that is currently active.

Parameters
[in]processor_handlea processor handle
[in]sensor_inda 0-based sensor index. Normally, this will be 0. If a device has more than one sensor, it could be greater than 0.
[in,out]statusa pointer to amdsmi_power_profile_status_t that will be populated by a call to this function If this parameter is nullptr, this function will return AMDSMI_STATUS_INVAL if the function is supported with the provided, arguments and AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the provided arguments.
Returns
amdsmi_status_t | AMDSMI_STATUS_SUCCESS on success, non-zero on fail