Windbg - <HRESULT 0x80004002>, invalid cast, alias as vs. aS - casting

Good day,
I have a Windbg script that iterates around the frames of a stack using a .do loop. For each frame it uses !for_each_local and $spat ("##Local","foo") to match for things I'm interested in getting an initial look at. I then use dx #$t2 = to assign and finally print out what I'm interested in, i.e. dx #$t2 = ((foobase*) this)->m_current->m_name - mostly this works fine.
Every now and again, there'll be a this in a frame that can't be cast successfully with the dx, so it's like barbase it would need, not foobase... the dx seems OK because I've wrapped it in a .foreach (output { dx #$t2 = ((foobase*) this)->m_current->m_name }) {} so it ends up containing something that causes the script to exit (that's despite trying a .catch).
At first I didn't really realise what was going on, but doing a .printf "%ma\n", #$t2 helped me to ascertain the contents is <HRESULT 0x80004002> which I think implies invalid cast - which, if so, makes perfect sense. My script line is using foobase when it would need barbase.
If I'm correct, what I'm looking for is a way to check that, before I delve into the script further and try to access details I know will cause the script to exit.
I seem to have come up with a convoluted way of doing it... but I wanted to ask if there was anything better...
.frame 0a $$ a frame with barbase, not foobase
dx #$t2 = ((foobase*) this)->m_name
.printf "%ma\n", #$t2 $$ prints <HRESULT 0x80004002>
as /c CastCheck .printf "%ma", #$t2
.if ($spat(#"${CastCheck}","<HRESULT 0x80004002>") = 1) { .printf "yes" } .else { .printf "no" } $$ prints yes
Is there a way to check if $t2 contains an indicator of invalid cast without using an alias?
I ask this because using the alias has caused me much confusion... in my script (for it to work, and it does seem to work quite well) I've had to use aS instead of as (I can use as fine from the Command Window, and I don't really understand why I need to change to aS from reading the documentation) and I also need to end the line with a semi-colon (unlike any other line in my script) and I need to use ad /q CastCheck; immediately before the aS, and then quickly afterwards, otherwise the alias seems to get lost, somehow... so you can tell the use of this alias has caused me a little trouble.
So, is there either a) an easy way to pre-check whether what I'm looking at is foobase or barbase, or b) post-check after the cast attempt whether $t2 contains this <HRESULT 0x80004002>? If I do a .printf "%d", #$t2 it shows 5... does 5 represent something too?
Or, indeed, any other ideas (or questions).
EDIT:
I'm going to try and illustrate what I mean with 2 short runs of code done in the Command Window... where frame 00 contains a this that can be cast to foobase and then the .printf works nicely, and where frame 0a contains a this that's a barbase (but my script still doesn't expect it and I want to be able to cater for it)... the dx still puts something in $t10... I want to be able to detect that it's garbage (or pre-detect I shouldn't even try the dx, if this is barbase, as it's pointless and I'm not interested in it anyway).
0:038> .frame 00
00 00000006`3e2bc930 00007ffe`926293a8 BlahBlahBlah
0:038> dx #$t10 = ((foobase *) this)->m_name
#$t10 = ((foobase *) this)->m_name : 0x869c7b8 : "nice string" [Type: char *]
0:038> .printf "%ma\n", #$t10
nice string
This is where the this is a barbase, and doesn't even have a m_name:
0:038> .frame 0a
0a 00000006`3e2bd220 00007ffe`91ec7780 BlahBlahBlah
0:038> dx #$t10 = ((foobase *) this)->m_name
#$t10 = ((foobase *) this)->m_name : 0x2265646f00000005 : "--- memory read error at address 0x2265646f`00000005 ---" [Type: char *]
0:038> .printf "%ma\n", #$t10
<HRESULT 0x80004002>
When put inside a loop I've found that the script will exit with a Memory access error on frame 0a when I try to check the length of the string I believe to have been returned by dx.
Basically, in the second block I want a way of either checking $t10 for something that is not a nice string (probably this <HRESULT 0x80004002>?) or, even better, not doing the dx and .printf at all, because I was able to check before that this was a barbase, not a foobase.
Is that making any more sense, or should I start again?

well you attempted but it is still vague
can you explain what is necessity to do .frame
and then dx this ??
you can cast any address to any type
for example in the first run I am casting the this to a proper type and enumerating all Frames
in the next run I am casting to a bogus type and enumerating
in both of this only one frame has a this pointer
when it is proper type it returns proper member
when it is bogus well it returns bogus stuff
0:000> !for_each_frame dx ((Student *) this)->Name
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
00 000000c5`d6f6f8c0 00007ff7`2818117c thisptr!Student::PrintStudent+0x9 [f:\src\thisptr\thisptr.cpp # 20]
((Student *) this)->Name : 0x7ff7282153e0 : "dave" [Type: char *]
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
01 000000c5`d6f6f8f0 00007ff7`281b6c20 thisptr!main+0x2c [f:\src\thisptr\thisptr.cpp # 33]
Error: Unable to bind name 'this'
it found a this but it cant find the Member Name
0:000> !for_each_frame dx ((ntdll!_EPROCESS *) this)->Name
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
00 000000c5`d6f6f8c0 00007ff7`2818117c thisptr!Student::PrintStudent+0x9 [f:\src\thisptr\thisptr.cpp # 20]
Error: Unable to bind name 'Name' <<<<<<
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
01 000000c5`d6f6f8f0 00007ff7`281b6c20 thisptr!main+0x2c [f:\src\thisptr\thisptr.cpp # 33]
Error: Unable to bind name 'this'
btw you know what type a this pointer is already so why are you casting it ??
you can find the type of this ptr with various commands some of which are shown below
0:000> dx this
this : 0xc5d6f6f910 [Type: Student *] <<<
[+0x000] Roll : 1 [Type: int]
[+0x008] Name : 0x7ff7282153e0 : "dave" [Type: char *]
[+0x010] Marks : 72.300000 [Type: double]
0:000> ?? this
class Student * 0x000000c5`d6f6f910 <<<
+0x000 Roll : 0n1
+0x008 Name : 0x00007ff7`282153e0 "dave"
+0x010 Marks : 72.299999999999997158
0:000> x /t this
000000c5`d6f6f8f0 class Student * this = 0x000000c5`d6f6f910 <<<
0:000> x /v /t this
prv local 000000c5`d6f6f8f0 8 class Student * this = 0x000000c5`d6f6f910
0:000> dt this
Local var # 0xc5d6f6f8f0 Type Student*
0x000000c5`d6f6f910
+0x000 Roll : 0n1
+0x008 Name : 0x00007ff7`282153e0 "dave"
+0x010 Marks : 72.299999999999997158
0:000> dt /v this
Local var [AddrFlags 90 AddrOff 0000000000000030 Reg/Val rsp (7)] # 0xc5d6f6f8f0 Type Student*
0x000000c5`d6f6f910 class Student, 5 elements, 0x18 bytes
+0x000 Roll : 0n1
+0x008 Name : 0x00007ff7`282153e0 "dave"
+0x010 Marks : 72.299999999999997158
<function> Student void (
int,
char*,
double)+000000c5`d6f6f910
<function> PrintStudent void ( void )+000000c5`d6f6f910
0:000> dt /v /t this
Local var [AddrFlags 90 AddrOff 0000000000000030 Reg/Val rsp (7)] # 0xc5d6f6f8f0 Type Student*
0x000000c5`d6f6f910 class Student, 5 elements, 0x18 bytes
+0x000 Roll : 0n1
+0x008 Name : 0x00007ff7`282153e0 "dave"
+0x010 Marks : 72.299999999999997158
<function> Student void (
int,
char*,
double)+000000c5`d6f6f910
<function> PrintStudent void ( void )+000000c5`d6f6f910

adding another answer to emphasize the usage of javascript instead of parsing text
javascript script support was added to windbg quiet a long time back
and it is being improved a lot
(to try the latest javascript additions use the windbg preview in latest windows 10 version)
x /v /t or dv /t provides the type of the this pointer
javascript provides a targetType property for these object
here is how to use it
create a .js file foo.js with the contents below
function typethis ( somevar )
{
host.diagnostics.debugLog( somevar.targetType , "\t" , JSON.stringify(somevar) , "\n" )
}
in windbg do
.load jsprovider
.scriptload x:\..\\..\foo.js
dx #$scriptcontents.functionname(argument) to run the script
and the results will be
0:000> .load jsprovider
0:000> .scriptload f:\wdscr\typthis.js
JavaScript script successfully loaded from 'f:\wdscr\typthis.js'
0:000> dx #$scriptContents.typethis( this )
Student * {"Roll":1,"Name":{},"Marks":72.3}
#$scriptContents.typethis( this )

Related

Yesod Persistent using Aeson to parse UTCTime into record

I have my model from models.persistentmodels
...
Thing
title Text
price Int
kosher Bool
optionalstuff [Text] Maybe
createdat UTCTime
updatedat UTCTime
deriving Show
...
It contains two time fields, which are UTCTime.
I am receiving via AJAX what is almost a Thing, in JSON. But the user JSON should not have createdat and updatedat or kosher. So we need to fill them in.
postNewEventR = do
inputjson <- requireCheckJsonBody :: Handler Value
...
-- get rawstringofthings from inputjson
...
let objectsMissingSomeFields = case (decode (BL.fromStrict $ TE.encodeUtf8 rawstringofthings) :: Maybe [Object]) of
Nothing -> error "Failed to get a list of raw objects."
Just x -> x
now <- liftIO getCurrentTime
-- Solution needs to go here:
let objectsWithAllFields = objectsMissingSomeFields
-- We hope to be done
let things = case (eitherDecode $ encode objectsWithAllFields) :: Either String [Thing] of
Left err -> error $ "Failed to get things because: " <> err
Right xs -> xs
The error "Failed to get things" comes here because the JSON objects we parsed are missing fields that are needed in the model.
Solution
let objectsWithAllFields = Import.map (tackOnNeccessaryThingFields now True) objectsMissingSomeFields
So we take the current object and tack on the missing fields e.g. kosher and createdat.
But there is some strange difference in the way UTCTime is read vs aeson's way to parse UTCTime. So when I print UTCTime in to a Aeson String, I needed to print out the UTCTime into the format that it is expecting later:
tackOnNeccessaryThingFields :: UTCTime -> Bool -> Object -> Object
tackOnNeccessaryThingFields t b hm = G.fromList $ (G.toList hm) <> [
("createdat", String (pack $ formatTime defaultTimeLocale "%FT%T%QZ" t)),
("updatedat", String (pack $ formatTime defaultTimeLocale "%FT%T%QZ" t)),
("kosher", Bool b)
]
tackOnNeccessaryThingFields _ _ _ = error "This isn't an object."
After this fix, the object has all the fields needed to make the record, so the code gives [Thing].
And also the code runs without runtime error, instead of failing to parse the tshow t as UTCTime.
Note:
This aeson github issue about this problem seems to be closed but it seems to be not any more permissive: https://github.com/bos/aeson/issues/197
Thanks to Artyom:
https://artyom.me/aeson#records-and-json-generics
Thanks to Pbrisbin:
https://pbrisbin.com/posts/writing_json_apis_with_yesod/
Thanks to Snoyman:
For everything

Recursive type not unifying with itself

The following code fails to compile with error:
type must be Tuple(Thing::Ish, Slice(UInt8)), not Tuple(Array(Array(Thing::Ish) | UInt8) | UInt8, Slice(UInt8))
These two types seem equivalent to me... and adding an .as(Ish) in the right place works... what am I missing? Why do these types not unify?
module Thing
alias Ish = UInt8 | Array(Ish)
def self.decode(bytes : Bytes) : {Ish, Bytes}
case bytes[0]
when 0x00..0x17
{bytes[0], bytes + 1}
when 0x18
MultiItemDecoder.new(0x80, ->(x: Bytes) { Thing.decode(x) }).decode(bytes)
else
raise "unknown"
end
end
class MultiItemDecoder(T)
def initialize(#base : UInt8, #item_decoder : Bytes -> {T, Bytes})
end
def decode(bytes): {Array(T), Bytes}
decode_some(bytes + 1, bytes[0])
end
def decode_some(bytes, n)
items = n.times.map do
item, bytes = #item_decoder.call(bytes)
item
end
{items.to_a, bytes}
end
end
end
This works:
module Thing
alias Ish = UInt8 | Array(Ish)
def self.decode(bytes : Bytes) : {Ish, Bytes}
case bytes[0]
when 0x00..0x17
{bytes[0], bytes + 1}
when 0x18
MultiItemDecoder.new(0x80, ->(x : Bytes) { Thing.decode(x) }).decode(bytes)
else
raise "unknown"
end
end
class MultiItemDecoder(T)
def initialize(#base : UInt8, #item_decoder : Bytes -> {T, Bytes})
end
def decode(bytes) : {Ish, Bytes}
decode_some(bytes + 1, bytes[0])
end
def decode_some(bytes, n)
items = n.times.map do
item, bytes = #item_decoder.call(bytes)
item.as(Ish)
end
{items.to_a.as(Ish), bytes}
end
end
end
Thing.decode(Bytes[1, 2, 3])
The thing is that Array(Array(Ish)) is not an Array(Ish), because for that it must be Array(Array(Ish) | UInt8) (note that it's an array of a union).
This all boils down to how things are represented in memory.
My advice is to avoid using recursive aliases. They are not intuitive and we might eventually remove them from the language.
Without seeing code to make this actually complile and get the error, self.decode wants to return a {Ish, Bytes}, which it does in when 0x00..0x17. But in 0x18 it is going to return an {Array(Ish), Bytes}. This will expand to {Array(UInt8 | Array(Ish)), Bytes} (recursively)

GDB (intel) : print works but not info address

The tree is as follows
module M
type A
real , allocatable :: dd (:)
end type A
type B
type (A) , pointer :: aa
end type B
type C
type(B) , pointer :: bb
end type C
type(C) , allocatable :: cc(:)
end module M
In gdb:
print M::cc(10) % bb % aa % dd(100)
$1 = 0
However,
info address M::cc(10) % bb % aa % dd (100)
generates the error: No symbol "cc(10) % bb % aa% dd (100)" in current context.
whatis address M::cc(10) % bb % aa% dd(100)
generates the error: Cannot access memory at address 0xa0
I see that the compiler flag "-g" has to be specified to access global variables but that didn't help.
The reason I need the address is to watch M::cc(10) %bb % aa % dd (100) with a software watchpoint so that I don't slow gdb drastically. I'm hoping that this would give me insight into the lines of code which change the value of dd (100).
One option that I've already explored is to just go ahead and set the watch point with:
watch cc(10) % bb % aa % dd ( 100 )
But this results in several hundred watchpoints with Numbers 1.1 , 1.2, 1.3 .... 1.500! This is really slowing down the execution in gdb.
So how could I access the address? If that is not possible, what is the best alternative?

Line truncated, Syntax error in argument list

When I compile the program below, I have an error and a warning in the call Coor_Trans command line as
Warning: Line truncated
Error: Syntax error in argument list
I compile the program several times, but it does not work. Maybe there is something wrong with my call command.
program 3D
implicit none
integer :: i,j,k
integer, parameter :: FN=2,FML=5,FMH=5
integer, parameter :: NBE=FN*FML*FMH
real, parameter :: pi = 4*atan(1.0)
real(kind=4), dimension(1:FN,1:FML+1,1:FMH+1) :: BEXL,BEYL,BEZL
real(kind=4), dimension(1:FN,1:FML,1:FMH) :: BEXC,BEYC,BEZC,BE2A,BE2B,ANGLE
real(kind=4), dimension(1:NBE,1:1,1:1) :: BEXC1,BEYC1,BEZC1,BE2A1,BE2B1,ANGLE1
real(kind=4), dimension(1:NBE,1:NBE) :: LOC_PTS1,LOC_PTS2,LOC_PTS3
real :: LOC_1,LOC_2,LOC_3
do i=1,FN
do j=1,FML
do k=1,FMH
BEXC(i,j,k) = 0.5*(BEXL(i,j,k) + BEXL(i,j+1,k))
BEYC(i,j,k) = 0.5*(BEYL(i,j,k) + BEYL(i,j+1,k))
BEZC(i,j,k) = 0.5*(BEZL(i,j,k) + BEZL(i,j,k+1))
BE2A(i,j,k) = FL(i)/FML + j*0 + k*0
BE2B(i,j,k) = FH(i)/FMH + j*0 + k*0
ANGLE(i,j,k) = BETA(i) + j*0 + k*0
end do
end do
end do
BEXC1 = reshape(BEXC,(/NBE,1,1/))
BEYC1 = reshape(BEYC,(/NBE,1,1/))
BEZC1 = reshape(BEZC,(/NBE,1,1/))
BE2A1 = reshape(BE2A,(/NBE,1,1/))
BE2B1 = reshape(BE2B,(/NBE,1,1/))
ANGLE1 = reshape(ANGLE,(/NBE,1,1/))
do i=1,NBE
do j=1,NBE
call Coor_Trans(BEXC1(i,1,1),BEYC1(i,1,1),BEZC1(i,1,1),BEXC1(j,1,1),BEYC1(j,1,1),BEZC1(j,1,1),ANGLE1(j,1,1),LOC_1,LOC_2,LOC_3)
LOC_PTS1(i,j) = LOC_1
LOC_PTS2(i,j) = LOC_2
LOC_PTS3(i,j) = LOC_3
end do
end do
end program 3D
subroutine Coor_Trans(GLOB_PTSX1,GLOB_PTSY1,GLOB_PTSZ1,GLOB_PTSX2,GLOB_PTSY2,GLOB_PTSZ2,BETA,LOC_PTS1,LOC_PTS2,LOC_PTS3)
implicit none
real(kind=4), intent(in) :: GLOB_PTSX1,GLOB_PTSY1,GLOB_PTSZ1,GLOB_PTSX2,GLOB_PTSY2,GLOB_PTSZ2,BETA
real(kind=4), intent(out) :: LOC_PTS1,LOC_PTS2,LOC_PTS3
real, parameter :: pi = 4*atan(1.0)
real :: E1,E2
E1 = cos(BETA/180*pi)
E2 = sin(BETA/180*pi)
LOC_PTS1 = (GLOB_PTSX1-GLOB_PTSX2)*E1 + (GLOB_PTSY1-GLOB_PTSY2)*E2
LOC_PTS2 = (GLOB_PTSZ1-GLOB_PTSZ2)
LOC_PTS3 = -(GLOB_PTSX1-GLOB_PTSX2)*E2 + (GLOB_PTSY1-GLOB_PTSY2)*E1
!return
end subroutine Coor_Trans
The length of your call statement is too long. The default maximum width of a line is 132.
The compiler will truncate input lines at that width [as it did--and said so with the warning]. After that, you had an incomplete line (e.g. call foo(a,b that was missing the closing )) which generated the second warning message.
The best solution is to break up the long line with a continuation character, namely &:
call Coor_Trans(BEXC1(i,1,1),BEYC1(i,1,1),BEZC1(i,1,1), &
BEXC1(j,1,1),BEYC1(j,1,1),BEZC1(j,1,1), &
ANGLE1(j,1,1),LOC_1,LOC_2,LOC_3)
Most C-style guides recommend keeping lines at <= 80 chars. IMO, that's a good practice even with fortran.
Note, with GNU fortran, you can increase the limit with the -ffree-line-length-<n> command line option. So, you could try -ffree-line-length-512, but, I'd do the continuation above
Historical footnote: 132 columns was the maximum width that a high speed, chain driven, sprocket feed, fanfold paper, line printer could print.
The Fortran standard imposes a limit on the length of line that compilers are required to deal with, these days it's 132 characters. You can break the line at a suitable place and use a continuation line. Something like this:
call Coor_Trans(BEXC1(i,1,1),BEYC1(i,1,1),BEZC1(i,1,1),BEXC1(j,1,1), &
BEYC1(j,1,1),BEZC1(j,1,1),ANGLE1(j,1,1),LOC_1,LOC_2,LOC_3)
Notice the & at the end of the continued line.
Once the line is truncated arbitrarily it is syntactically erroneous, which explains the second part of your compiler's complaint.
Your compiler probably has an option to force it to read longer lines.

Print Value to File

i was not able to print an a value (float) to a file with the OCaml lenguage.
How can i do?
If you know how, can you show me a little example?
Thank you advance and have a good day!
Printf.fprintf allows direction to an out_channel, in your case a file. Reasonably, you'd open the file for writing first, and pass around that channel.
Printf.fprintf (open_out "file.txt") "Float Value of %f" 1.0
If you want to print the textual representation of a float to a file, perhaps the simplest thing to do is:
output_string outf (string_of_float myfloat)
If you want to print the float to the console, you can use
print_string (string_of_float myfloat)
Of course, Printf.printf can also do that and more, so it is worth knowing it.
If you want to output the binary representation of a float, things are more complicated. Since a float value is represented as an IEEE 754 double, it is 8 bytes long which can be written in different orders depending on the platform. In the case of little-endian order, as is normal in X86, you can use the following:
let output_float_le otch fv =
let bits = ref (Int64.bits_of_float fv) in
for i = 0 to 7 do
let byte = Int64.to_int (Int64.logand !bits 0xffL) in
bits := Int64.shift_right_logical !bits 8;
output_byte otch byte
done
The float value so written can be read back with the following:
let input_float_le inch =
let bits = ref 0L in
for i = 0 to 7 do
let byte = input_byte inch in
bits := Int64.logor !bits (Int64.shift_left (Int64.of_int byte) (8 * i))
done;
Int64.float_of_bits !bits
This has the advantage of being a very compact way to exactly preserve floats in a file, that is, what you write will be read back exactly as it originally was. For example, I did this in the interactive top-level:
# let otch = open_out_bin "Desktop/foo.bin" ;;
val otch : out_channel = <abstr>
# output_float_le otch 0.5 ;;
- : unit = ()
# output_float_le otch 1.5 ;;
- : unit = ()
# output_float_le otch (1. /. 3.) ;;
- : unit = ()
# close_out otch ;;
- : unit = ()
# let inch = open_in_bin "Desktop/foo.bin" ;;
val inch : in_channel = <abstr>
# input_float_le inch ;;
- : float = 0.5
# input_float_le inch ;;
- : float = 1.5
# input_float_le inch ;;
- : float = 0.333333333333333315
# close_in inch ;;
- : unit = ()
and as you can see I got back exactly what I put in the file. The disadvantage of this form of writing floats to files is that the result is not human-readable (indeed, the file is binary by definition) and you lose the possibility to interoperate with other programs, like Excel for instance, which in general exchange data in human-readable textual form (CSV, XML, etc.).
Did you try printf?
let a = 4.0
printf "my float value: %f" a
Cf the doc inria and don't forget to open the module