Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,15 @@ sphinx-build -M html doc build --keep-going
```bash
pyinstaller --noconfirm .\pyx2cscope_win.spec
```

## Creating artifacts to upload to github release page
This script will execute the pyinstaller command listed above,
include the script file to start the web interface, zip
the contents of the dist folder and add the whell file
available on pypi in the dist folder.

```bash
python -m scripts/build.py
```

## Creating artifacts to upload to GitHu
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "pyx2cscope"
version = "0.5.0"
version = "0.5.1"
description = "python implementation of X2Cscope"
authors = [
"Yash Agarwal",
Expand Down Expand Up @@ -32,11 +32,10 @@ pyyaml ="^6.0.1"
numpy = "^1.26.0"
matplotlib = "^3.7.2"
PyQt5 = "^5.15.9"
pyqtgraph= "^0.13.7"
pyqtgraph = "^0.13.7"
mchplnet = "0.3.0"
flask = "^3.0.3"


[tool.ruff]
line-length = 120
# only allow the specified pydocstyle convention
Expand Down
12 changes: 2 additions & 10 deletions pyx2cscope/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
"""This module contains the pyx2cscope package.

Version: 0.5.0
Version: 0.5.1
"""

# Apply eventlet monkey patch before any other imports if web interface is requested
import sys

if "-w" in sys.argv or "--web" in sys.argv:
import eventlet
eventlet.monkey_patch()

import logging

__version__ = "0.5.0"

__version__ = "0.5.1"

def set_logger(
level: int = logging.ERROR,
Expand Down
63 changes: 63 additions & 0 deletions pyx2cscope/examples/array_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""PyX2CScope array example reference.

This example shows different use cases for single and multidimensional arrays.

We define 3 different arrays:

static uint8_t my_array1D[3] = {3, 2, 1};
static uint8_t my_array2D[2][3] = { {6, 5, 4}, {3, 2, 1} };
static uint8_t my_array3D[2][2][3] = {
{
{12, 11, 10}, {9, 8, 7},
},
{
{6, 5, 4}, {3, 2, 1},
}
};
"""

import logging

from pyx2cscope.utils import get_com_port, get_elf_file_path
from pyx2cscope.x2cscope import X2CScope

# Set up logging
logging.basicConfig(
level=logging.INFO,
filename=__file__ + ".log",
)

# X2C Scope Set up
elf_file = get_elf_file_path()
com_port = get_com_port()
x2c_scope = X2CScope(port=com_port, elf_file=elf_file)

my_array_1d = x2c_scope.get_variable("my_array1D")
my_array_2d = x2c_scope.get_variable("my_array2D")
my_array_3d = x2c_scope.get_variable("my_array3D")

print(my_array_1d.get_value())
# [3, 2, 1]
print(my_array_2d.get_value())
# [6, 5, 4, 3, 2, 1]
print(my_array_3d.get_value())
# [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

my_array_2d_10 = x2c_scope.get_variable("my_array2D[1][0]")
my_array_2d_10.set_value(10)
print(my_array_2d.get_value())
# [6, 5, 4, 10, 2, 1]

my_array_2d[4] = 11
print(my_array_2d.get_value())
# [6, 5, 4, 10, 11, 1]

print(my_array_2d[5])
# 1

my_array_3d_102 = x2c_scope.get_variable("my_array3D[1][0][2]")
my_array_3d_102.set_value(10)
print(my_array_3d.get_value())
# [12, 11, 10, 9, 8, 7, 6, 5, 10, 3, 2, 1]


29 changes: 0 additions & 29 deletions pyx2cscope/examples/testingArray.py

This file was deleted.

7 changes: 2 additions & 5 deletions pyx2cscope/gui/web/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
This module holds and handles the main url and forward the relative urls to the specific
pages (blueprints).
"""
import eventlet

eventlet.monkey_patch()

import logging
import os
import webbrowser
Expand Down Expand Up @@ -176,7 +172,8 @@ def main(host="localhost", web_port=5000, new=True, *args, **kwargs):
print("Server is open for external requests!")

if os.environ.get('DEBUG') != 'true':
socketio.run(app, debug=False, host=host, port=web_port)
socketio.run(app, debug=False, host=host, port=web_port,
allow_unsafe_werkzeug=True)
else:
socketio.run(app, debug=True, host=host, port=web_port,
allow_unsafe_werkzeug=True, use_reloader=False)
Expand Down
31 changes: 8 additions & 23 deletions pyx2cscope/gui/web/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,16 @@
This module provides SocketIO configuration and lock creation functions
that adapt to the current environment (production or debug mode).
"""
import os

from flask_socketio import SocketIO

# Only enable eventlet in production, not during debugging
if os.environ.get('DEBUG', None) is None: # None means production
import eventlet
eventlet.monkey_patch()
socketio = SocketIO(cors_allowed_origins="*", async_mode='eventlet')

def create_lock():
"""Create an eventlet-based semaphore lock.
socketio = SocketIO(cors_allowed_origins="*", async_mode='threading')
import threading

Returns:
eventlet.semaphore.Semaphore: A semaphore lock for thread synchronization.
"""
return eventlet.semaphore.Semaphore()
else:
socketio = SocketIO(cors_allowed_origins="*", async_mode='threading')
import threading

def create_lock():
"""Create a threading-based lock.
def create_lock():
"""Create a threading-based lock.

Returns:
threading.Lock: A lock for thread synchronization.
"""
return threading.Lock()
Returns:
threading.Lock: A lock for thread synchronization.
"""
return threading.Lock()
5 changes: 2 additions & 3 deletions pyx2cscope/gui/web/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
This module provides the WebScope class for managing watch and scope variables
through the web interface.
"""
import numbers
import time

from pandas.core.dtypes.inference import is_number

from pyx2cscope.gui.web import extensions
from pyx2cscope.x2cscope import TriggerConfig, X2CScope

Expand Down Expand Up @@ -127,7 +126,7 @@ def set_watch_rate(self, rate):
Args:
rate (float): Polling rate in seconds (must be between 0 and MAX_WATCH_RATE).
"""
if is_number(rate) and 0 < rate < self.MAX_WATCH_RATE:
if isinstance(rate, numbers.Number) and 0 < rate < self.MAX_WATCH_RATE:
self.watch_rate = rate

def clear_watch_var(self):
Expand Down
28 changes: 18 additions & 10 deletions pyx2cscope/parser/generic_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"""

import logging
import math
from itertools import product

from elftools.construct.lib import ListContainer
from elftools.dwarf.dwarf_expr import DWARFExprParser
Expand Down Expand Up @@ -106,6 +108,7 @@ def _get_base_type_die(self, current_die):
if type_attr:
ref_addr = type_attr.value + current_die.cu.cu_offset
return self.dwarf_info.get_DIE_from_refaddr(ref_addr)
return None

def _get_end_die(self, current_die):
"""Find the end DIE of a type iteratively."""
Expand Down Expand Up @@ -224,7 +227,8 @@ def _process_array_type(self, end_die, member_name, offset):
"""
members = {}
array_members = {}
array_size = self._get_array_length(end_die)
array_dimensions = self._get_array_dimensions(end_die)
array_size = math.prod(array_dimensions)
base_type_die = self._get_base_type_die(end_die)
self._process_end_die(members, base_type_die, member_name, offset)
if members:
Expand All @@ -241,11 +245,13 @@ def _process_array_type(self, end_die, member_name, offset):
}

# Generate array members, e.g.: array[0], array[1], ..., array[i]
for i in range(array_size):
ranges = [range(d) for d in array_dimensions]
for idx, idx_tuple in enumerate(product(*ranges)):
idx_str = ''.join(f'[{i}]' for i in idx_tuple)
for name, values in members.items():
element_name = name.replace(member_name, f"{member_name}[{i}]")
element_name = name + idx_str
array_members[element_name] = values.copy()
array_members[element_name]["address_offset"] += i * idx_size
array_members[element_name]["address_offset"] += idx * idx_size

return array_members

Expand Down Expand Up @@ -344,16 +350,18 @@ def _process_structure_type(self, die, parent_name: str, offset=0):
return members

@staticmethod
def _get_array_length(type_die):
"""Gets the length of an array type."""
array_length = 0
def _get_array_dimensions(type_die):
"""Gets the length of an array type.

Multidimensional arrays have multiple children with the tag DW_TAG_subrange_type.
"""
dimensions = []
for child in type_die.iter_children():
if child.tag == "DW_TAG_subrange_type":
array_length_attr = child.attributes.get("DW_AT_upper_bound")
if array_length_attr:
array_length = array_length_attr.value + 1
break
return array_length
dimensions.append(array_length_attr.value + 1)
return dimensions

@staticmethod
def _process_base_type(end_die, parent_name, offset):
Expand Down
12 changes: 10 additions & 2 deletions pyx2cscope_win.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ a = Analysis(
['pyx2cscope\\__main__.py'],
pathex=[],
binaries=[],
datas=[('pyx2cscope\\gui\\web\\static', 'pyx2cscope\\gui\\web\\static'), ('pyx2cscope\\gui\\web\\templates', 'pyx2cscope\\gui\\web\\templates'), ('pyx2cscope\\gui\\img', 'pyx2cscope\\gui\\img')],
hiddenimports=[],
datas=[
('pyx2cscope\\gui\\web\\static', 'pyx2cscope\\gui\\web\\static'),
('pyx2cscope\\gui\\web\\templates', 'pyx2cscope\\gui\\web\\templates'),
('pyx2cscope\\gui\\img', 'pyx2cscope\\gui\\img'),
],
hiddenimports=[
'engineio.async_threading',
'engineio.async_drivers.threading'
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
Expand Down Expand Up @@ -35,6 +42,7 @@ exe = EXE(
entitlements_file=None,
icon=['pyx2cscope\\gui\\img\\pyx2cscope.ico'],
)

coll = COLLECT(
exe,
a.binaries,
Expand Down
Binary file modified requirements.txt
Binary file not shown.
Loading