Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: "FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory" on convoluted locators #34059

Open
DetachHead opened this issue Dec 17, 2024 · 0 comments

Comments

@DetachHead
Copy link
Contributor

DetachHead commented Dec 17, 2024

Version

1.49.1

Steps to reproduce

test('asdf', async ({ page }) => {
    const locator = page.locator(
        'text=L1 >> internal:or="text=L2 >> internal:or=\\"text=L3 >> internal:or=\\\\\\"text=L4\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L5 >> internal:or=\\\\\\\\\\\\\\"text=L6\\\\\\\\\\\\\\"\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L7 >> internal:or=\\\\\\\\\\\\\\"text=L8\\\\\\\\\\\\\\"\\\\\\"\\"" >> internal:or="text=L9 >> internal:or=\\"text=L10 >> internal:or=\\\\\\"text=L11\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L12 >> internal:or=\\\\\\\\\\\\\\"text=L13\\\\\\\\\\\\\\"\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L14 >> internal:or=\\\\\\\\\\\\\\"text=L15\\\\\\\\\\\\\\"\\\\\\"\\"" >> internal:or="text=L16 >> internal:or=\\"text=L17 >> internal:or=\\\\\\"text=L18\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L19 >> internal:or=\\\\\\\\\\\\\\"text=L20\\\\\\\\\\\\\\"\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L21 >> internal:or=\\\\\\\\\\\\\\"text=L22\\\\\\\\\\\\\\"\\\\\\"\\"" >> internal:or="text=L23 >> internal:or=\\"text=L24 >> internal:or=\\\\\\"text=L25\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L26 >> internal:or=\\\\\\\\\\\\\\"text=L27\\\\\\\\\\\\\\"\\\\\\" >> internal:or=\\\\\\"#f0 >> internal:control=enter-frame >> #f0_mid_0 >> internal:control=enter-frame >> text=L28 >> internal:or=\\\\\\\\\\\\\\"text=L29\\\\\\\\\\\\\\"\\\\\\"\\""',
    )
    await locator.count()
})

(note that this locator is not realistic but rather just something intentionally convoluted we were able to come up with to be able to reliably reproduce the issue. see the Additional context section below for more info about my use case)

Expected behavior

no crash

Actual behavior

<--- Last few 
GCs --->

[53800:000001F794E39CA0]    39636 ms: Scavenge 4020.3 (4111.3) -> 4020.0 (4122.3) MB, 11.8 / 0.0 ms  (average mu = 0.596, current mu = 0.478) allocation failure; 
[53800:000001F794E39CA0]    39652 ms: Scavenge 4027.0 (4122.3) -> 4027.6 (4123.1) MB, 12.5 / 0.0 ms  (average mu = 0.596, current mu = 0.478) allocation failure; 
[53800:000001F794E39CA0]    39871 ms: Scavenge 4027.8 (4123.1) -> 4026.9 (4146.1) MB, 218.8 / 0.0 ms  (average mu = 0.596, current mu = 0.478) allocation failure; 


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

 1: 00007FF6515FDABF 
node_api_throw_syntax_error+175503
 2: 00007FF651582496 v8::base::CPU:
:num_virtual_address_bits+66006
 3: 00007FF651583853 v8::base::CPU::num_virtual_address_bits+71059
 4: 00007FF6520BC811 v8::Isolate::ReportExternalAllocationLimitReached+65
 5: 00007FF6520A7466 v8::internal::
V8::FatalProcessOutOfMemory+662
 6: 00007FF651F0DB70 v8::internal::EmbedderStackStateScope::ExplicitScopeForTesting+144
 7: 00007FF651F0AB8D v8::internal::Heap::CollectGarbage+4749
 8: 00007FF651F207B6 v8::internal::HeapAllocator::Alloca
teRawWithLightRetrySlowPath+2150
 9: 00007FF651F210EF v8::internal::HeapAllocator::AllocateRawWithRetryOrFailSlowPath+95
10: 00007FF651F30310 v8::internal::Factory::NewFillerObject+448
11: 00007FF651BE5CE5 v8::inte
rnal::Runtime::SetObjectProperty+20997
12: 00007FF652165E61 v8::internal::SetupIsolateDelegate::SetupHeap+606705
13: 00007FF652183237 v8::internal::SetupIsolateDelegate::SetupHeap+726471
14: 00007FF5D2421215 

Additional context

you're probably wondering why on earth i'm using a locator like this.

in my project we're using playwright to automate sap webgui and ui5 elements, which are notorious for having extremely convoluted generated html that's difficult to write locators for. as a result, we've created some high level wrapper classes on top of playwright locators to make it much easier for developers to interact with them.

one example is these tables. not only is it difficult to associate a cell with a column, but scrolling re-renders each row with new data instead of each row being its own element, meaning we need to implement custom logic to iterate over them. we have a Table class to interact with these tables:

table = Table.with_column("Customer")
for row in table.rows():
    if row.cell("Customer").value == "Example Corp. (J001)":
        ...

under the hood, the Table class uses a locator which is constructed from several different sources:

class Table(Ui5Element):
    def __init__(self, locator: Locator):
        self.locator = locator.and_(cls.control_type())

    @override
    @classmethod
    def control_type(self) -> str:
        return "sap.m.Table"


    @classmethod
    def with_column(cls, name: str):
        locator = Column(name) # separate class for column with its own logic to construct locators
        return cls(locator)

these classes have multiple different ways to construct them and interact with the elements, which can result in some of the locators they generate being quite complicated.

this example is pseudocode as this is from a private codebase, but i hope it's able to at least provide a bit of an explanation as to why we're using such convoluted locators (they're considered a low-level implementation detail for us rather than something that the developer has to regularly interact with). either way i don't think they're convoluted enough to warrant using several gigabytes of memory to evaluate them, so i still believe this is a bug in playwright

Environment

System:
    OS: Windows 11 10.0.22621
    CPU: (12) x64 11th Gen Intel(R) Core(TM) i5-11500H @ 2.90GHz
    Memory: 8.62 GB / 31.20 GB
  Binaries:
    Node: 22.12.0 - ~\project\.venv\Scripts\node.EXE
    npm: 10.9.0 - ~\project\.venv\Scripts\npm.EXE
  IDEs:
    VSCode: 1.96.0 - C:\Users\user\AppData\Local\Programs\Microsoft VS Code\bin\code.CMD
  npmPackages:
    playwright-ui5: ^1.6.4 => 1.6.5 
    playwright-xpath: ^1.2.0 => 1.2.0
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

No branches or pull requests

1 participant