Skip to content

Conversation

@aallrd
Copy link

@aallrd aallrd commented May 23, 2023

Hello,
I would like to contribute the below features:

  • source path substitution
    • The source files used to build the application will not always be in the expected location.
    • This is the same feature as the gdb substitution path to locate source files on disk.
  • relative source filename display
    • The full path of the source files can be long and meaningless (see above).
    • This feature allows to display only relative paths, removing a list of prefixes.
  • custom HTML report title
    • Self explanatory, ability to change the title of the HTML report from the default "ValgrindCI report"

Copy link
Owner

@bcoconni bcoconni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for submitting a PR @aallrd, this is much appreciated.
I have made a number of comments in your code, could you please address them ?

In particular your code is rightfully rejected by mypy which reports 5 errors. I have mentioned them in my comments, please fix them.

Comment on lines +47 to +56
#### Build and package an executable with `pyinstaller`

You can use `pyinstaller` to create a single-file executable binary:

```bash
> pip install pyinstaller
> pyinstaller --onefile --add-data ValgrindCI:ValgrindCI valgrind-ci
> ./dist/valgrind-ci --help
```

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of including this piece of documentation ? Which audience is it intended to ?

Comment on lines +83 to +85
for s in args.substitute_path:
substitute_paths.append({"from": s.split(":")[0], "to": s.split(":")[1] })
data.set_substitute_paths(substitute_paths)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s.split(':') is executed twice which is inefficient. Please use local variables instead:

Suggested change
for s in args.substitute_path:
substitute_paths.append({"from": s.split(":")[0], "to": s.split(":")[1] })
data.set_substitute_paths(substitute_paths)
for s in args.substitute_path:
_from, _to = s.split(':')
substitute_paths.append({"from": _from, "to": _to})
data.set_substitute_paths(substitute_paths)

Comment on lines +87 to +91
if args.relativize:
prefixes = []
for p in args.relativize:
prefixes.append(p)
data.set_relative_prefixes(prefixes)
Copy link
Owner

@bcoconni bcoconni May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you duplicating the list args.relativize in another list prefixes ? Wouldn't it be simpler to send args.relativize as an argument to data.set_relative_prefixes() ?

Suggested change
if args.relativize:
prefixes = []
for p in args.relativize:
prefixes.append(p)
data.set_relative_prefixes(prefixes)
if args.relativize:
data.set_relative_prefixes(args.relativize)

Comment on lines +121 to +122
self._substitute_paths: Optional[List[dict]] = []
self._relative_prefixes: Optional[List[str]] = []
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these members declared Optional[] ? It doesn't seem to me they can ever be None...

Suggested change
self._substitute_paths: Optional[List[dict]] = []
self._relative_prefixes: Optional[List[str]] = []
self._substitute_paths: List[dict] = []
self._relative_prefixes: List[str] = []

self._source_dir = None

def set_substitute_paths(self, substitute_paths: Optional[List[dict]]) -> None:
if substitute_paths is not None:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the parameter substitute_paths ever be None ? Why is substitute_paths of type Optional[} ?

return path

def relativize(self, path: str) -> str:
for p in self._relative_prefixes:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mypy reports an error for this line of code: ValgrindCI/parse.py:149: error: Item "None" of "Optional[List[str]]" has no attribute "__iter__" (not iterable) [union-attr]

Please fix it.

Comment on lines +151 to +153
path = path.replace(p, "")
if path.startswith("/"):
path = path[1:]
Copy link
Owner

@bcoconni bcoconni May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use òs.path.commonprefix()andos.path.relpath()` which are portable across OSes rather than homemade code:

Suggested change
path = path.replace(p, "")
if path.startswith("/"):
path = path[1:]
if os.path.commonprefix([p, path]) == p:
path = os.path.relpath(path, p)

stack["fileref"] = "{}:{}".format(frame_source, error_line)
fullname = self._data.substitute_path(fullname)
try:
with open(fullname, "r", errors="replace") as f:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of using errors="replace" when reading a file ?

if l >= stack["line"] and l <= error_line + lines_after - 1:
stack["code"].append(code_line)
except OSError as e:
print(f"Warning: cannot read stack data from missing source file: {e.filename}")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't continue be called when swallowing this exception in order to avoid adding a malformed "stack" to the issue dictionary ?

Suggested change
print(f"Warning: cannot read stack data from missing source file: {e.filename}")
print(f"Warning: cannot read stack data from missing source file: {e.filename}")
continue

src_data, l + 1, lines_before, lines_after
filename = self._data.substitute_path(filename)
try:
with open(filename, "r", errors="replace") as f:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There again, what is the point of using errors="replace" when reading a file ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants