This is an archive of the discontinued LLVM Phabricator instance.

[lldb/crashlog] Refactor the CrashLogParser logic
ClosedPublic

Authored by mib on Aug 3 2022, 10:56 AM.

Details

Summary

This patch changes the CrashLogParser class to be both the base class
and a Factory for the JSONCrashLogParser & TextCrashLogParser.

That should help remove some code duplication and ensure both class
have a parse method.

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>

Diff Detail

Event Timeline

mib created this revision.Aug 3 2022, 10:56 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 3 2022, 10:56 AM
mib requested review of this revision.Aug 3 2022, 10:56 AM
kastiglione added inline comments.Aug 3 2022, 11:20 AM
lldb/examples/python/crashlog.py
420–424

It looks like debugger, path, verbose are not propagated to the CrashLogParser subclass, is this an oversight or is there some magic? Secondly, args and kwargs used/needed?

lldb/examples/python/scripted_process/crashlog_scripted_process.py
12–14

Is this catch and re-raise of any use? Since you're refactoring, maybe delete it?

JDevlieghere added inline comments.Aug 3 2022, 11:23 AM
lldb/examples/python/crashlog.py
419
433

Can this stay in the JSONCrashLogParser?

629
lldb/examples/python/scripted_process/crashlog_scripted_process.py
13

Given that the module itself is called crashlog, I would keep the underscore to avoid potential naming collisions.

mib added inline comments.Aug 3 2022, 11:32 AM
lldb/examples/python/crashlog.py
433

I need it to get the exception dictionary for D131086 ...

mib marked 4 inline comments as done.Aug 3 2022, 6:16 PM
mib added inline comments.
lldb/examples/python/crashlog.py
433

I took another look at this ... it seems that TextCrashLogParser doesn't set the data or exception attributes ... I'd need first to have both parsers produce the same exception dictionary before being able to use it. I'll do that on a follow-up patch.

mib updated this revision to Diff 449847.Aug 3 2022, 6:16 PM
mib marked an inline comment as done.

Address @JDevlieghere comments

JDevlieghere added inline comments.Aug 4 2022, 9:25 AM
lldb/examples/python/crashlog.py
419

This is not done (yet)

mib updated this revision to Diff 450694.Aug 7 2022, 9:48 PM
mib marked 2 inline comments as done.

Address @kastiglione comment

kastiglione added inline comments.Aug 8 2022, 3:12 PM
lldb/examples/python/crashlog.py
418–419

I have not seen the object().__new__(SomeClass) syntax. Why is it being used for TextCrashLogParser but not JSONCrashLogParser? Also, __new__ is a static method, could it be object.__new__(...)? Or is there a subtly that requires an object instance? Somewhat related, would it be better to say super().__new__(...)?

Also: one class construction explicitly forwards the arguments, the other does not. Is there a reason both aren't implicit (or both explicit)?

421–424

Is this comment needed (maybe it was in an earlier draft)? The body of the function isn't calling init, so it might be fine to not have it.

kastiglione added inline comments.Aug 8 2022, 3:16 PM
lldb/examples/python/crashlog.py
440

can this be an __init__?

mib marked 3 inline comments as done.Aug 8 2022, 4:47 PM
mib added inline comments.
lldb/examples/python/crashlog.py
418–419

As you know, python class are implicitly derived from the object type, making object.__new__ and super().__new__ pretty much the same thing.

In this specific case, both the TextCrashLogParser and JSONCrashLogParser inherits from the CrashLogParser class, so JSONCrashLogParser will just inherits CrashLogParser.__new__ implementation if we don't override it, which creates a recursive loop.
That's why I'm calling the __new__ method specifying the class.

440

No unfortunately because at the __init__ is called, the type of the object is already set, but we actually want to fallback to the TextCrashLogParser class if this one fails.

__new__: takes arguments and returns an instance
__init__: takes instance and arguments (from __new__) and returns nothing

JDevlieghere added inline comments.Aug 8 2022, 5:37 PM
lldb/examples/python/crashlog.py
418–419

What's the advantage of this over this compared to a factory method? Seems like this could be:

def create(debugger, path, verbose)
    try:
            return JSONCrashLogParser(debugger, path, verbose)
        except CrashLogFormatException:
            return  TextCrashLogParser(debugger, path, verbose)
mib marked 2 inline comments as done.Aug 8 2022, 6:00 PM
mib added inline comments.
lldb/examples/python/crashlog.py
418–419

If we make a factory, then users could still call __init__ on CrashLogParser and create a bogus object. With this approach, they're forced to instantiate a CrashLogParser like any another object.

kastiglione added inline comments.Aug 9 2022, 8:36 AM
lldb/examples/python/crashlog.py
418–419

CrashLogParser.__init__ could raise an exception. With intricacy of this approach, maybe it's better to use a factor method combined with an exception if the base class __init__ is called.

JDevlieghere added inline comments.Aug 9 2022, 9:25 AM
lldb/examples/python/crashlog.py
418–419

+1, or maybe abc provide a capability to achieve the same?

mib marked 2 inline comments as done.Aug 9 2022, 10:06 AM
mib added inline comments.
lldb/examples/python/crashlog.py
418–419

IMHO, having to call an arbitrary-called method (create/make/...) to instantiate an object and having the __init__ raise an exception introduces more intricacies in the usage of this class, compared to what I'm doing.

I prefer to keep it this way since it's more natural / safe to use. If the implementation exercises some python internal features, that's fine because that shouldn't matter to the user.

kastiglione added inline comments.Aug 9 2022, 11:21 AM
lldb/examples/python/crashlog.py
418–419

Only after discussing it with you, and reading python docs, do I understand why this code is the way it is. Future editors, including us, could forget some details, which isn't great for maintainability.

You mention the user, are there external users of this class hierarchy? Or are these classes internal to crashlog.py? If the latter, then the simplified interface seems hypothetical. If there are external users, how many are they? I am trying to get a sense for what is gained by the avoiding a factory method.

kastiglione added inline comments.Aug 9 2022, 11:32 AM
lldb/examples/python/crashlog.py
418–419

here's an idea that may simplify things:

instead of embedding validation inside JSONCrashLogParser.__new__, have a static/class method for validation. Then, the base class can decide which subclass by calling the validation method. This means the subclasses don't need to override __new__. Ex:

class Base:
  def __new__(cls, i: int):
    if Sub1.is_valid(i):
      return object.__new__(Sub1)
    else:
      return object.__new__(Sub2)

  def __init__(self, i: int):
    print("Base", i)

class Sub1(Base):
  @staticmethod
  def is_valid(i: int):
    return i == 1234

  def __init__(self, i: int):
    super().__init__(i)
    print("Sub1", i)

class Sub2(Base):
  def __init__(self, i: int):
    super().__init__(i)
    print("Sub2", i)

was the chmod intentional?

mib added a comment.Aug 9 2022, 1:28 PM

was the chmod intentional?

nop, thanks for catching that

mib added inline comments.Aug 9 2022, 1:31 PM
lldb/examples/python/crashlog.py
418–419

@JDevlieghere might be able to give more details on our users but crashlog.py could be imported in other python script, and from what he told me, we have some internal users that rely on it.

I don't have any strong opinion wrt your suggestion, I'll try it out and update the patch if that works well. Thanks @kastiglione !

mib updated this revision to Diff 451266.Aug 9 2022, 2:03 PM
mib marked 3 inline comments as done.

Implement @kastiglione suggestion

kastiglione accepted this revision.Aug 9 2022, 3:57 PM

thanks for having patience with us reviewers, lgtm with one comment about python versions

lldb/examples/python/crashlog.py
421

the walrus operator (:=) is python 3.8 or newer, just want to make sure we don't expect this code to run in 3.6 or other earlier versions

This revision is now accepted and ready to land.Aug 9 2022, 3:57 PM
mib updated this revision to Diff 451306.Aug 9 2022, 4:37 PM
mib marked an inline comment as done.

Remove walrus (:=) operator

mib added a comment.Aug 9 2022, 4:38 PM

thanks for having patience with us reviewers, lgtm with one comment about python versions

Thanks for the review :) !

This revision was landed with ongoing or failed builds.Aug 9 2022, 9:02 PM
This revision was automatically updated to reflect the committed changes.