text: Title: Enhance recursion detection
  Body: It is easy to create infinite recursion with Robot:
  ```robotframework
  *** Test Cases ***
  Example
      Recursion
  
  *** Keywords ***
  Recursion
      Recursion
  ```
  
  To prevent recursive execution failing on Python side for `RecursionError` in an unexpected location, which typically breaks output.xml, we have a hard limit of 100 recursive keyword or control structures. The limit was added in RF 2.7 (#551) and it was initially 42, but it was raised to 100 in RF 5.0 (#4191) when we didn't need to anymore think about Jython.
  
  A recent change to listeners (#5268) allowed listeners to get notifications from actions they initiated which may cause recursion with listeners. For example, this one keeps calling `Log` forever (i.e. until recursion is forcefully stopped):
  ```python
  from robot.libraries.BuiltIn import BuiltIn
  
  def start_keyword(data, result):
      BuiltIn().run_keyword('Log', 'Recursion!')
  ```
  
  Interestingly in this usage the current mechanism to detect recursion doesn't work. As the result, execution using the above listeners fails so that output.xml is corrupted. The reason seems to be that there are more Python stack frames used when listeners are involved and a simple fix would be lowering the current limit of 100 started keywords or control structures to something like 75. I guess that would be fine, but there could be someone who's affected by the change.
  
  An alternative fix is coming up with a better way to detect recursion. Instead of having a hard limit of started keywords or control structures, we could look are we close to Python's recursion limit. That was actually mentioned as an option already in #551, but [mentioned in a comment](https://github.com/robotframework/robotframework/issues/551#issuecomment-47478924), using `len(inspect.stack())` would be really slow. Good news is that I found a more performant way to do that using [sys._getframe](https://docs.python.org/3/library/sys.html#sys._getframe). `sys._getframe` isn't guaranteed to exist on all Python implementations, but it exists at least in PyPy and it's easy to handle it not existing in others.