Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2026-05

Question: ref counts / strings, smart pointers...

7 messages · 2 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin Frb

I know how refcount internally work on strings and dyn array.

I am thinking about formulating a request to the DWARF specs. So I want to check the details, and also get information how they
- will work for smart pointers,
- where else they may occur?

Background, refcounts may be of interest for the debugger in at least the following cases.
- A user modifies a variable (or an indexed value) via the debugger.   ~ The debugger may additionally need info, if it should "copy on write" / though that can be derived....
  ~ The debugger could inform the user about "side effects on other references to the value"
- The user may want to explicitly see the refcount.   ~ A user may have "pchar" access to  a string, and the user wants to ensure the string was correctly "uniquestring" treated.
- A user wants to call a function that has a string/array as argument/result.
  ~ The debugger must deal with that, without introducing mem leaks.

If I am correct, then the following dwarf tags may need information

DW_TAG_Array (string and array)
DW_TAG_string_type
DW_TAG_structure // or class / interface => for smart pointers // other types? base type, e.g. a refcounted char or integer???

The last assumes that the info can be encoded to the "smart pointed to" structure?
- Are there non-structure values that could be affected?
- Should maybe the DW_TAG_Pointer hold the refcount info instead?

I would expect the ref-count to be in the target value? Unless you can have a non-ref-counted structure, but have a ref-counted pointer to it????

------------------
The "copy on write" can be known by checking if it is a string. That is, as long as smart pointers will not look like a string.

The debugger likely will need some knowledge on the producer (and then such type based decisions can be allowed)>
This is because the debugger must also know how to call fpc_ansistring_decref etc...

And trying to encode what functions to call (including where to pass which data as parameter =>probably way to complex)

So my current idea is to request a simple
  DW_AT_refcount  // location, reference, constant // maybe flag maybe
  DW_AT_refcount_size / bit_size

(And maybe similar for the encoding field... any idea on what other fields may be of interest, or any that are going to be added soon-ish?)

------------------
I am not sure yet which methods the debugger may have to call on smart pointers, or what else it would need to know.
Any feedback on that part would be welcome

Reply by email

Tomas Hajny
to Martin Frb
Martin Frb wrote 14 lines
I know how refcount internally work on strings and dyn array. I am thinking about formulating a request to the DWARF specs. So I want to check the details, and also get information how they - will work for smart pointers, - where else they may occur? Background, refcounts may be of interest for the debugger in at least the following cases. - A user modifies a variable (or an indexed value) via the debugger.   ~ The debugger may additionally need info, if it should "copy on write" / though that can be derived....   ~ The debugger could inform the user about "side effects on other references to the value"

.
.

I can't imagine a reasonable scenario when a user might want to perform a modification of a refcounted variable of the program being debugged by using "copy on write". I strongly believe that such an option should not be offered to the far as modifying the already existing instance is concerned, the debugger should probably always check whether the shared reference is still valid and having the original value at the time of trying to modify it - trying to add the debugger as another reference of an existing refcounted variable might IMHO lead to unexpected/unwanted effects on the program being debugged and thus should be avoided as well.

Just my 2 cents

Tomas

Reply by email

Martin Frb
to Tomas Hajny
Tomas Hajny wrote 21 lines
On 2026-05-27 12:11, Martin Frb via fpc-devel wrote: > I know how refcount internally work on strings and dyn array. > > I am thinking about formulating a request to the DWARF specs. So I > want to check the details, and also get information how they > - will work for smart pointers, > - where else they may occur? > > Background, refcounts may be of interest for the debugger in at least > the following cases. > - A user modifies a variable (or an indexed value) via the debugger. >   ~ The debugger may additionally need info, if it should "copy on > write" / though that can be derived.... >   ~ The debugger could inform the user about "side effects on other > references to the value"  .  . I can't imagine a reasonable scenario when a user might want to perform a modification of a refcounted variable of the program being debugged by using "copy on write".

If the user assigns a new (char) value to
  MyAnsiString[3]

then they might expect that.
After all, this is what would have happened, had there been code doing   MyAnsiString[3] := '?';

At that point, a user might not even remember that the text content of MyAnsiString may use the same memory as some other string var that they have.

For dynamic arrays, of course the correct behaviour is to modify all variables that share the memory. Same as we do with objects (class instances), for which there isn't ever a refcount and it couldn't even be known.

---------------
So for copy on write: this is about ansistrings. But the debugger must know that it has a refcounted string. => and e.g for wide-types there are
- widestring (afaik not refcounted on Windows)
- unicodestring (refcounted)

And trying to detect that based on coincidental other criteria.... not the best idea.

Show quoted text 8 lines
I strongly believe that such an option should not be offered to the far as modifying the already existing instance is concerned, the debugger should probably always check whether the shared reference is still valid and having the original value at the time of trying to modify it - trying to add the debugger as another reference of an existing refcounted variable might IMHO lead to unexpected/unwanted effects on the program being debugged and thus should be avoided as well.

As for the new smart pointers, I haven't played with them yet. I don't expect that there will be any special behaviour on the debugger side for the "modify" feature.

But....

1) The debugger has the :ref intrinsic that currently allows the user to peek at the refcount of string and dyn-array => that can be useful. I could imagine if you have leaks with smart pointers, then you may want to trace this.

2) The debugger may need to create/destroy data, if it passes it to function calls in watches (or dec-ref results).
So it does need to know if there is a ref-count.
Once it knows the type is refcounted, it should hopefully be able to decide the correct function based on knowing what the compiler does for a given type.

Reply by email

Tomas Hajny
to Martin Frb
Martin Frb wrote 29 lines
On 27/05/2026 12:29, Tomas Hajny via fpc-devel wrote: > On 2026-05-27 12:11, Martin Frb via fpc-devel wrote: >> I know how refcount internally work on strings and dyn array. >> >> I am thinking about formulating a request to the DWARF specs. So I >> want to check the details, and also get information how they >> - will work for smart pointers, >> - where else they may occur? >> >> Background, refcounts may be of interest for the debugger in at least >> the following cases. >> - A user modifies a variable (or an indexed value) via the debugger. >>   ~ The debugger may additionally need info, if it should "copy on >> write" / though that can be derived.... >>   ~ The debugger could inform the user about "side effects on other >> references to the value" >  . >  . > > I can't imagine a reasonable scenario when a user might want to > perform a modification of a refcounted variable of the program being > debugged by using "copy on write". If the user assigns a new (char) value to   MyAnsiString[3] then they might expect that. After all, this is what would have happened, had there been code doing   MyAnsiString[3] := '?';

Yes, but there is an important difference there. If there's code modifying the value, this code uses this copy from that point on. However, if a user wants to modify some data in a debugger, this user probably wants to do it in order of his code started to use the new value instead of the original value, not because of being interested in having another copy of the original data with a different value (which wouldn't be used by the debugged code any longer).

At that point, a user might not even remember that the text content of MyAnsiString may use the same memory as some other string var that they have.

Yes - it's absolutely appropriate for the debugger to be aware of the refcounting and warning the user if the reference counter is higher than 1 and the user intends to modify the value.

Show quoted text 6 lines
--------------- So for copy on write: this is about ansistrings. But the debugger must know that it has a refcounted string. => and e.g for wide-types there are - widestring (afaik not refcounted on Windows) - unicodestring (refcounted)

Yes, I don't want to question the necessity of understanding the refcounting for the debugger.

And trying to detect that based on coincidental other criteria.... not the best idea.

Correct.

.
.

Show quoted text 10 lines
As for the new smart pointers, I haven't played with them yet. I don't expect that there will be any special behaviour on the debugger side for the "modify" feature. But.... 1) The debugger has the :ref intrinsic that currently allows the user to peek at the refcount of string and dyn-array => that can be useful. I could imagine if you have leaks with smart pointers, then you may want to trace this.

Yes, peeking the refcount may be indeed useful, that's for sure.

Show quoted text 6 lines
2) The debugger may need to create/destroy data, if it passes it to function calls in watches (or dec-ref results). So it does need to know if there is a ref-count. Once it knows the type is refcounted, it should hopefully be able to decide the correct function based on knowing what the compiler does for a given type.

Function calls in watches are very tricky in any case and allowing them in case of reference counted parameters sounds like a recipe for problems as far as I can tell (I can imagine quite a few cases when this may lead to complete change of the behaviour of the program being debugged). If nothing else, the debugger should warn the users trying to do that.

Tomas

Reply by email

Martin Frb
to Tomas Hajny
Tomas Hajny wrote 19 lines
>> >> I can't imagine a reasonable scenario when a user might want to >> perform a modification of a refcounted variable of the program being >> debugged by using "copy on write". > > If the user assigns a new (char) value to >   MyAnsiString[3] > > then they might expect that. > After all, this is what would have happened, had there been code doing >   MyAnsiString[3] := '?'; Yes, but there is an important difference there. If there's code modifying the value, this code uses this copy from that point on. However, if a user wants to modify some data in a debugger, this user probably wants to do it in order of his code started to use the new value instead of the original value, not because of being interested in having another copy of the original data with a different value (which wouldn't be used by the debugged code any longer).

I am not sure if I follow...

procedure DoFoo(s: AnsiString);
begin
    ...
   // paused here
    ...
    if s = ... then
    ...
end;

if the user tells the debugger to modify
  s[1] to be '?'

I.e. in the Lazarus IDE would open the "Evaluate/Modify" window, enter: s[1] and set the new value to '?'.

Then that user most likely does not want the variable in the caller to be changed?

Of course if he had code where within a function the string was "duplicated"

   MyAnsiStrFoo := MyAnsiStrBar;
   // paused here

then both might be possible. Albeit, I find it more likely that the user would still wont just one string to be modified.
But if the request was to have an option to modify all strings sharing that memory, then ok (the user could always do ^char(MyAnsiStrFoo)[0]  // index one less)

Show quoted text 7 lines
> At that point, a user might not even remember that the text content > of MyAnsiString may use the same memory as some other string var that > they have. Yes - it's absolutely appropriate for the debugger to be aware of the refcounting and warning the user if the reference counter is higher than 1 and the user intends to modify the value.

In any case, there can be options for various preferences. "power users" may indeed want to be more "memory orientated". So that option should probably exist.

Show quoted text 15 lines
> 2) The debugger may need to create/destroy data, if it passes it to > function calls in watches (or dec-ref results). > So it does need to know if there is a ref-count. > Once it knows the type is refcounted, it should hopefully be able to > decide the correct function based on knowing what the compiler does > for a given type. Function calls in watches are very tricky in any case and allowing them in case of reference counted parameters sounds like a recipe for problems as far as I can tell (I can imagine quite a few cases when this may lead to complete change of the behaviour of the program being debugged). If nothing else, the debugger should warn the users trying to do that.

I don't use them myself. I got burned back in the days of Delphi 5 (iirc). When I couldn't get the bug to trigger under the debugger, because I was watching
  SomeControl.Handle
(the property with getter)
And hence the debugger did create the handle, and the error was gone in the debugger.

But...
- there is a huge amount of people who deeply believe they can't live without function calls (including property getters)
- who have not yet been burned, or if they have they are not aware of it
- who will not believe that it will ever happen to them.

So well, let them have it. They have been warned...

----------------

Currently, in the few cases where FpDebug can detect an AnsiString as such (Dwarf-3), it can handle it in function calls (definitely as result, iirc as param too, including from constant in the user expression). And FpDebug does do the correct additional calls to the rec cnt functions. (via the linker symbols, that usually are available)

Adding the tag will ensure FpDebug can more accurately/safely detect such strings.

Reply by email

Tomas Hajny
to Martin Frb
Martin Frb wrote 42 lines
On 27/05/2026 13:03, Tomas Hajny via fpc-devel wrote: >>> >>> I can't imagine a reasonable scenario when a user might want to >>> perform a modification of a refcounted variable of the program being >>> debugged by using "copy on write". >> >> If the user assigns a new (char) value to >>   MyAnsiString[3] >> >> then they might expect that. >> After all, this is what would have happened, had there been code >> doing >>   MyAnsiString[3] := '?'; > > Yes, but there is an important difference there. If there's code > modifying the value, this code uses this copy from that point on. > However, if a user wants to modify some data in a debugger, this user > probably wants to do it in order of his code started to use the new > value instead of the original value, not because of being interested > in having another copy of the original data with a different value > (which wouldn't be used by the debugged code any longer). I am not sure if I follow... procedure DoFoo(s: AnsiString); begin    ...   // paused here    ...    if s = ... then    ... end; if the user tells the debugger to modify   s[1] to be '?' I.e. in the Lazarus IDE would open the "Evaluate/Modify" window, enter: s[1] and set the new value to '?'. Then that user most likely does not want the variable in the caller to be changed?

Actually, I'd expect that _if_ the user wants to modify the value within the debugger, he wants to have full picture of effects of the modified value rather than just parcial effect which would never match real case scenario when the variable has a different value. Moreover, creating a copy for the modified version wouldn't change the reference used in the code of DoFoo, would it? Otherwise you'd need to modify not only the value, but also all parts of code in DoFoo retrieving this value to point to the new copy instead, right?

.
.

Show quoted text 23 lines
> Function calls in watches are very tricky in any case and allowing > them in case of reference counted parameters sounds like a recipe for > problems as far as I can tell (I can imagine quite a few cases when > this may lead to complete change of the behaviour of the program being > debugged). If nothing else, the debugger should warn the users trying > to do that. I don't use them myself. I got burned back in the days of Delphi 5 (iirc). When I couldn't get the bug to trigger under the debugger, because I was watching   SomeControl.Handle (the property with getter) And hence the debugger did create the handle, and the error was gone in the debugger. But... - there is a huge amount of people who deeply believe they can't live without function calls (including property getters) - who have not yet been burned, or if they have they are not aware of it - who will not believe that it will ever happen to them. So well, let them have it. They have been warned...

OK, understood, we clearly see it the same way.

Show quoted text 10 lines
---------------- Currently, in the few cases where FpDebug can detect an AnsiString as such (Dwarf-3), it can handle it in function calls (definitely as result, iirc as param too, including from constant in the user expression). And FpDebug does do the correct additional calls to the rec cnt functions. (via the linker symbols, that usually are available) Adding the tag will ensure FpDebug can more accurately/safely detect such strings.

OK, that indeed makes sense.

Tomas

Reply by email

Martin Frb
to Tomas Hajny
Tomas Hajny wrote 23 lines
On 2026-05-27 13:45, Martin Frb via fpc-devel wrote: > > procedure DoFoo(s: AnsiString); > begin >    ... >   // paused here >    ... >    if s = ... then >    ... > end; > .... > Then that user most likely does not want the variable in the caller > to be changed? Actually, I'd expect that _if_ the user wants to modify the value within the debugger, he wants to have full picture of effects of the modified value rather than just parcial effect which would never match real case scenario when the variable has a different value. Moreover, creating a copy for the modified version wouldn't change the reference used in the code of DoFoo, would it? Otherwise you'd need to modify not only the value, but also all parts of code in DoFoo retrieving this value to point to the new copy instead, right?

Yes, the idea would be that telling the debugger do "set s[1] to be '?'" would
- check the refcount
- if 1 => then modify directly
- if 2 or greater => then
  - create a new string in the target memory
  - change the char
  - point "s" to that new string
  - decref the old string
  // all this could alternatively be a call to "uniquestring" followed by the modification

And that would also deal with a refcount of -1 (a VARIABLE, but pointing to a constant string) => which should probably ask by default.

If that is done, then the code in "DoFoo" would continue, and "s" would have the new value.
But the caller would still the unmodified value.

---------------------
Also I completely forgot (and currently IIRC the debugger rejects it entirely)

Tell the debugger to modify: MyAnsiStr := 'abc'
Then the old value needs a decref, and a new string must be created.

But if it isn't refcounted, then the old value must not be touched, just the new one needs to be allocated in mem, and pointed to) // and well that may leak, if the user doesn't know what they do...

---------------------
Generally we have 2 kind of users (more, but lets simplify)

1) the beginner-like user
Someone who might not even know that FPC uses copy on write. Someone who truly thinks that after "s1 := s2;" they have 2 completely unrelated values.

2) the experienced user
Someone who knows what happens under the hood, and may make use of that within their code. And of course during debugging.

Show quoted text 13 lines
> ---------------- > Currently, in the few cases where FpDebug can detect an AnsiString as > such (Dwarf-3), it can handle it in function calls (definitely as > result, iirc as param too, including from constant in the user > expression). > And FpDebug does do the correct additional calls to the rec cnt > functions. (via the linker symbols, that usually are available) > > Adding the tag will ensure FpDebug can more accurately/safely detect > such strings. OK, that indeed makes sense.

Well, and hence, back to my original question...

Any ideas what I should consider when formulating a feature request for additions to the DWARF standard.

And for that, the idea is to make sure that
 === The information can be added to debug info should that be needed ===

Once it can be added, we can still decide
- what we add
- how we use it

I want to avoid requesting it now, and then find that I forgot some. E.g. I forgot that you can have properties like
  TFoo = class
     FList = array [0..9] of TPoint;
    property X2: integer read Flist[2].x;  // yes, that works   end;
That doesn't even need function calls..
=> well that can be encoded as workaround "tag_member x2" / but then looses the info its a property

the DW_TAG_property support the field ".x", but not the index on the field var. (it supports other indices)

-------------------------------

So how are ref counts used with e.g. smart pointers. What data types will there by in smart pointers
What operations happen on smart pointers and there data

Reply by email