# scope_profiler.native_samples

*module*

Line-level profiles of native (C) regions, from the sampler's ``.sps`` files.

With sampling on (``sp_start_sampling()`` or ``SCOPE_PROFILER_SAMPLE_HZ``), the
C library records the interrupted instruction address and the innermost open
region at every timer tick, and ``sp_finalize()`` writes them to
``<prefix>_rank<NNNNN>.sps`` next to the trace. This module reads that file and
maps the addresses to ``file:line`` using the program's debug info (build with
``-g``), so the work happens after the run, not in it.

Layout, native byte order, as written by ``sp_finalize``::

    char[8]   "SCOPESMP"
    int32     format version (1)
    int32     rank
    int32     sampling rate [Hz]
    int64     timer ticks in total, whether or not they landed in a region
    int32     number of regions
    per region:  int32 name length, char[] name
    int32     number of modules
    per module:  int32 path length, char[] path, uint64 load address
    int64     number of samples
    per sample:  int32 region, int32 module (-1 if unknown), uint64 address

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L1-L1)

## scope_profiler.native_samples.KNOWN_FORMAT_VERSIONS

*attribute* · *module attribute*

```python
KNOWN_FORMAT_VERSIONS = (1)
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L37-L37)

## scope_profiler.native_samples.MAGIC

*attribute* · *module attribute*

```python
MAGIC = b'SCOPESMP'
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L35-L35)

## scope_profiler.native_samples.SAMPLES_SUFFIX

*attribute* · *module attribute*

```python
SAMPLES_SUFFIX = '.sps'
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L36-L36)

## scope_profiler.native_samples.LineStat

*class* · *dataclass*

```python
class LineStat
```

Samples attributed to one source line of one region.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L58-L65)

### scope_profiler.native_samples.LineStat.file

*attribute* · *instance attribute*

```python
file: str
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L62-L62)

### scope_profiler.native_samples.LineStat.function

*attribute* · *instance attribute*

```python
function: str
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L64-L64)

### scope_profiler.native_samples.LineStat.line

*attribute* · *instance attribute*

```python
line: int
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L63-L63)

### scope_profiler.native_samples.LineStat.samples

*attribute* · *instance attribute*

```python
samples: int
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L65-L65)

## scope_profiler.native_samples.SampleFile

*class* · *dataclass*

```python
class SampleFile
```

One rank's raw samples.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L46-L55)

### scope_profiler.native_samples.SampleFile.hz

*attribute* · *instance attribute*

```python
hz: int
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L51-L51)

### scope_profiler.native_samples.SampleFile.modules

*attribute* · *instance attribute*

```python
modules: list
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L54-L54)

### scope_profiler.native_samples.SampleFile.rank

*attribute* · *instance attribute*

```python
rank: int
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L50-L50)

### scope_profiler.native_samples.SampleFile.regions

*attribute* · *instance attribute*

```python
regions: list
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L53-L53)

### scope_profiler.native_samples.SampleFile.samples

*attribute* · *class attribute* · *instance attribute*

```python
samples: np.ndarray = field(repr=False)
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L55-L55)

### scope_profiler.native_samples.SampleFile.ticks

*attribute* · *instance attribute*

```python
ticks: int
```

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L52-L52)

## scope_profiler.native_samples.SampleFormatError

*class*

```python
class SampleFormatError(ValueError)
```

Bases: `ValueError`

A file is not a scope-profiler sample file, or is truncated.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L42-L43)

## scope_profiler.native_samples.SymbolizerError

*class*

```python
class SymbolizerError(RuntimeError)
```

Bases: `RuntimeError`

No tool is available to map addresses to source lines.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L144-L145)

## scope_profiler.native_samples.find_sample_files

*function*

```python
def find_sample_files(inputs) -> list
```

Collect ``.sps`` files from paths, directories, or a mix of both.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L125-L141)

## scope_profiler.native_samples.format_line_profile

*function*

```python
def format_line_profile(profile: dict, top: int = 10, source: bool = True) -> str
```

Render :func:`line_profile` output as plain-text tables.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L251-L282)

## scope_profiler.native_samples.line_profile

*function*

```python
def line_profile(samples: SampleFile) -> dict
```

Aggregate a rank's samples into per-line statistics for each region.

Returns
-------
dict
    ``{region name: [LineStat, ...]}``, hottest line first. Counts are
    samples of the line itself; code without debug info (a library such as
    libm) is grouped under ``??:0``. The timer's effective rate can be
    below the requested one, so shares are reported rather than seconds.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L213-L248)

## scope_profiler.native_samples.read_samples

*function*

```python
def read_samples(path) -> SampleFile
```

Read one ``.sps`` file written by ``sp_finalize()``.

Raises
------
SampleFormatError
    If the file is not a sample file, is truncated, or is of a newer
    format version.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L68-L122)

## scope_profiler.native_samples.symbolize

*function*

```python
def symbolize(module: str, base: int, addresses: list) -> list
```

Map absolute addresses in one module to ``(function, file, line)``.

Needs the program built with ``-g``; without debug info the line is 0.

[View source](https://github.com/max-models/scope-profiler/blob/devel/src/scope_profiler/native_samples.py#L196-L210)
