The entire discussion only applies to unsafe languages, the ones that dump core. By which I mean, C. Or C++, if you're really out of luck.
If it can dump core, it will dump core, unless it decides to silently corrupt its data instead. Trust my experience of working in a multi-processor, multi-threaded, multi-programmer, multi-nightmare environment. Some C++ FQA Lite readers claimed that the fact that I deal with lots of crashes in C++ indicates that I'm a crappy programmer surrounded by similar people. Luckily, you don't really need to trust my experience, because you can trust Google's. Do this:
- Find a Google office near you.
- Visit a Google toilet.
- You'll find a page about software testing, with the subtitle "Debugging sucks. Testing rocks." Read it.
- Recover from the trauma.
- Realize that the chances of you being better at eliminating bugs than Google are low.
- Read about the AdWords multi-threaded billing server nightmare.
- The server was written in C++. The bug couldn't happen in a safe language. Meditate on it.
- Consider yourself enlightened.
This isn't the reason why this post has "Google core dump" in its title, but hopefully it's a reason for us to agree that your C/C++ program will crash, too.
I love globals
What happens when we face a core dump? Well, we need the same things you'd expect to look for in any investigation: names and addresses. Names of objects looking at which may explain what happened, their addresses to actually look at them, and type information to sensibly display them.
In C and C++, we have 3 kinds of addresses: stack, heap and global. Let's see who lives there.
Except the stack is overwritten, because it can be. Don't count on being able to see the function calls leading to the point of crash, nor the parameters and local variables of those functions. In fact, don't even count on being able to see the point of crash itself: the program counter, the link register, the frame pointer, all that stuff can contain garbage.
And the heap is overwritten, too, nearly as badly. The typical data structure used by C/C++ allocators (for example, dlmalloc) is a kind of linked list, where each memory block is prefixed with its size so you can jump to the next one. Overwrite one of these size values and you will have lost the boundaries of the chunks above that address. That's a loss of 50% of the heap objects on average, assuming uniform distribution of memory overwriting bugs across the address space of the heap.
So don't count on the stack or the heap. Your only hope is that someone has ignored the Best Practices and the finger-pointing by the more proficient colleagues, and allocated a global object. Possibly under the clever disguise of a "Singleton". Not a bad thing after all, that moronic "design pattern", because it ultimately allowed to counter cargo cult programmers' accusations of "globals are evil" with equally powerful cargo cult argument of "it's a design pattern". So people could allocate globals again.
Which is good, because a global always has an accurate name-to-address mapping, no matter what atrocity was committed by the bulk of unsafe code running on the loose. Can't overwrite a symbol table. And it has accurate type information, too. As opposed to objects you find through void*, or a base class pointer where the base class lacks virtual functions or the object vptr was overwritten, etc.
Which is why I frequently start debugging by firing an object view window on a global, or running debugger macros which read globals, etc. Of course you can fuck up a global variable to make debugging unpleasant. For example, if the variable is "static" in the C sense, you need to open the right file or function to display it, and you need the debugger front-end to understand the context, which will be especially challenging if it's a static variable in a template function (one of the best things in C++ is how neatly its new features interact with C's old ones).
Or you can stuff the global into a class or a namespace. I was never able to display globals by their qualified C++ name in, say, gdb 5. But no matter; nm <program> | grep <global>
followed by p *(TypeOfGlobal*)addr
always does the trick, and no attempts at obfuscating the symbol table will stop it. I still say make it a real, unashamed global to make debugging easier. If you're lucky, you'll get to piss off a couple of cargo cult followers as a nice side-effect.
Google Core Dump
A core dump is a web. Its sites are objects. It's hyperlinks are pointers. It's PageRank is a TypeRank: what's the type of this object according to the votes of the pointers stored in other objects? The spamdexing is done by pointer-like bit patterns stored in unused memory slots. The global variables are the major sites with high availability you can use as roots for the crawling.
What utilities would we like to have for this web? The usual stuff.
- Browsers. Debugger object view window is the Firefox, and the memory view window is the Lynx. The core dump Lynx usually sucks in that it doesn't make it easy to follow pointers – can't click on a word and have the browser follow the pointer (by jumping to the memory pointed by it). No back button, either. Oh well.
- DNS. The ability to translate variable names to raw addresses. Works very reliably for globals and passably otherwise. Works reliably for all objects in safe languages.
- Reverse DNS. Given an address, tell me the object name. Problematic for dynamically allocated objects, although you could list the names of pointer variables leading to it (Google bombing). Works reliably for global functions and variables. For some reason, the standard addr2line program only supports functions though. Which is why I have an addr2sym program. It so happened that I have several of them, in fact. You can download one here. "Reverse DNS" is particularly useful when you find pointers somewhere in registers or memory and wonder what they could point to. In safe languages, you don't have that problem because everything is typed and so you can simply display the pointed object.
- Google Core Dump, similar to Google Desktop or Google for the WWW. Crawl a core dump, figure out the object boundaries and types by parsing the heap linked list and the stack and looking at pointers' "votes", create an index, and allow me to query that index. Lots of work, that, some of it heuristical. And in order to get type information in C or C++, you'll have to either parse the source code (good luck doing it with C++), or parse the non-portable debug information format. But it's doable; in fact, we have it, for our particular target/debugger/allocator combo. Of course it has its glitches. Quirky and obscure enough to make open sourcing it not worth the trouble.
I really wish there was a reasonably portable and reliable Google Core Dump kind of thing. But it doesn't look like that many people care about debugging crashes at all. Most core dumps at customer sites seem to go to /dev/null, and those that can't be easily deciphered are apparently given up on until the bug manifests itself in some other way or its cause is guessed by someone.
Am I coming from a particularly weird niche where the code size is large enough and the development rapid enough to make crashes almost unavoidable, but crashes in the final product version are almost intolerable? Or do most good projects allocate everything on the stack and the heap, so with those smashed they're doomed no matter what? Or is the problem simply stinky enough to make it unattractive for a hobby project while lacking revenue potential to make a good commercial project?
Would you like this sort of thing? If you would, drop me a line. In the meanwhile, I satisfy my wish for a Google Core Dump with my perfect implementation for an embedded co-processor, the one I've poked at with Tcl commands. With 128K of memory, no dynamic allocation, and local variables effectively implemented as globals, perfect decoding is easy. I'm telling ya, globals rule.
As to my "reverse DNS" implementation:
- I could make it more portable by parsing the output of
nm --print-size
. But just running nm on a 20M symbol table takes about 2 seconds. I want instantaneous output, 'cause I'm very impatient when I debug. - Alternatively, I could make it more portable by using a library such as bfd. But that would drag in a library such as bfd, and I had trouble with what looked like library/compiler version mismatches with bfd, whereas my ELF parsing code never had any trouble. Also, an implementation parsing ELF is more interesting as sample code because you get to see how easy to parse these formats are. So it's elfaddr2sym, not addr2sym. (It's really 32-bit-ELF-with-host-endianness-addr2sym, because I'm lazy and it covers all my targets.)
- There's a ton of addr2sym code out there, and maybe a good addr2sym program. I just didn't find it. I have an acknowledged weakness in the wheel reinventing department.
- Of course I don't demangle the ugly C++ names; piping to c++filt does.
- The program is in D, because of the "instantaneous" bit, and because D is one of the best choices available today if you care about both speed and brevity. Look at this:
lowerBound!("a.st_value <= b")(ssyms, addr)
does a binary search for addr in the sorted ssyms array. As brief as it gets out of the box with any language and standard library, isn't it? The string is compiled statically into the instantiation of the lowerBound template; a & b are the arguments of the anonymous function represented by the string. Readable. Short. Fast. Easy to use – garbage-collected array outputs in functions like filter(), error messages to the point – that's why a decent grammar is a good thing even if you aren't the compiler writer. Looks a lot like C++, braces, static typing, everything. Thus easy to pimp in a 3GL environment, in particular, a C++ environment. You can download the Digital Mars D compiler for Linux, or wait for C++0x to solve 15% of the problems with<algorithm>
by introducing worse problems.
By the way, the std.algorithm module, the one with the sort, filter, lowerBound and similar functions, is by Andrei Alexandrescu, of Modern C++ Design fame. How is it possible that his stuff in D is so yummy while his implementation of similar things in C++ is equally icky? Because C++ is to D what proper fixation is to anaesthesia. There, I bet you saw it coming.
What does "global" mean?
For the sake of completeness, I'd like to bore you with a discussion of the various aspects of globalhood, in the vanishingly small hope of this being useful in a battle against a cargo cult follower authoring a coding convention or such. In C++, "global" can mean at least 6 things:
- Number of instances per process. A "global" is everything that's instantiated once.
- Life cycle. A "global" is constructed before main and destroyed after main. A static variable inside a function is not "global" in this sense.
- "Scope" in the "namespace" sense (as opposed to the life cycle sense). We have C-style file scope, class scope, function scope, and "the true global scope". And we have namespaces.
- Storage. A "global" is assigned a link time address and stored there. In a singleton implementation calling new and assigning its output to a global pointer, the pointer is "global" in this sense but the object is not.
- Access control. If it's in a class scope, it may be private or protected, which makes it less of a global in this fifth sense.
- Responsibility. A global can be accessible from everywhere but only actually accessed from a couple of places. For example, you can allocate a large object instantiating lots of members near your main function and then call object methods which aren't aware that the stuff is allocated globally.
So when I share my love of globals with you, the question is which aspect of globality I mean. What I mean is this:
- I like global storage – link-time addresses – for everything which can be handled that way. A global pointer is better than nothing, but it can be overwritten and you will have lost the object; better allocate the entire thing globally.
- I like global scope, no classes, namespaces and access control keywords attached, to make symbol table look-up easier, thus making use of the global allocation.
- I like global life cycle – no Meyers' singletons and lazy initialization. In fact, I like trivial constructors/destructors, leaving the actual work to init/close functions called by main(). This way, you can actually control the order in which things are done and know what the dependencies are. With Meyers' singletons, the order of destruction is uncontrollable (it's the reverse order of initialization, which doesn't necessarily work). Solutions were proposed to this problem, so dreadful that I'm not going to discuss them. Just grow up, design the damned init/close sequence and be in control again. Why do people think that all major operations should be explicit except for initialization which should happen automagically when you least expect it?
- "Globals" in the sense of "touched by every piece of code" is the trademark style of a filthy swine. There are plenty of good reasons to use "globals"; none of them has anything to do with "globals" as in "variables nobody/everybody is responsible for".
- I think that everything that's instantiated once per process is a "global", and when you wrap it with scope, access control, and design patterns, you shouldn't stop calling it a global (and instead insist on "singleton", "static class member", etc.). It's still a global, and its wrapping should be evaluated by its practical virtues. Currently, I see no point in wrapping globals in anything – plain old global variables are the thing best supported by all software tools I know.
I think this can be used as "rationale" in a coding guideline, maybe in the part allowing the use of globals as an "exception". But I keep my hopes low.
134 comments ↓
[...] Linker>> saved by stefaanh 22 days ago3 votesProfile simuPOP>> saved by fadereu 28 days ago2 votesI love globals, or Google Core Dump>> saved by sanyaissues 41 days ago3 votesApple patches 25 flaws with latest update>> saved by fenec [...]
The Google bug mentioned here is plain stupidity. To say that it can happen only in C++… is even worse. The same can happen in C (as you put it… any unsafe language). However, any decent C/C++ (multi-threaded… not necessarily) app developer must know… you don't use a pointer (or a boost::ref) to a stack allocated object in one thread from another. Nor can you use the same and put in a list and later de-reference it.
It is our stupidity if we do that… not the fault of C++. You learn lessons as you do serious development work. You make mistakes and correct them.
Every decent Java programmer knows that Java programs can have memory leaks and they learn it through experience. It is newbies that swallow the whole "there are no memory leaks in Java" nonsense without looking deeper.
BTW… multi-threading is hard. Not just in C/C++. You have to go the "no mutable data structures" to get it right (easily that is).
You know which part I liked best? The "…or a boost::ref" part.
Want! I want that debugger! I once worked in a place where globals were used with abandon in the last sense. It was terrible. The boss man once actually said something like "Does anyone actually use those?" about a core dump.
@Nathan: I'm not sure I understood, but – are you saying that you want a program for figuring out the types and locations of live objects in a core dump, what I called Google Core Dump above?
I hope my boss man reads this. My company <3 globals, "singletons" and static member variables. It seems like I am the only one at the company that gets that they are all the same thing. The only thing that changes is how it is accessed. But restricting the access still doesn't actually impose any real restrictions.
Yes, I want a Google Core Dump.
Wow, that's a long pause in a conversation… well, you're the first to want it, I think; not that I have any – we had something basically working for one of our obscure platforms but it broke down due to size issues in the next revision of that platform.
Read the Linux Kernel Style Guide. Then try writing a bit of C in that style yourself… a kernel module, perhaps.
Try this, and you will achieve enlightenment. And you'll never want to write a line of C++ again.
P.S. The best kind of globals are the ones that are static to file scope.
Um… What if I already don't want to write a line of C++ again? Looks like you're preaching to the choir, probably unintentionally.
As to static globals – theoretically great, but some software tools have trouble with those. So I prefer global visibility and a naming convention to prevent clashes.
Personally, I read all the best C++ books — Scott Meyers, Joshuttis, etc. I learned all their recommendations by heart, and I put them into practice too.
However, it wasn't until I wrote code in the Linux kernel style that I really knew what good low-level code looked like. Just to take one feature, the 8-space tabs acted as a check on excessive nesting and overlong functions.
I realized then that C++ was just a framework that had gotten too big for its own good. The OO framework that the Linux kernel guys wrote (take a look at KObject, for example), is actually safer and saner than C++'s horrible vtable clusterfuck.
Over time, people have realized that composition of multiple objects is often a better way to go than inheritance. But C++ provides lots and lots of syntax for creating deep inheritance hierarchies, and almost none for composing objects. Consider how tedious it is to write a wrapper class or even to implement accessor functions in C++.
So to summarize: to a first approximation, C++ consists of an obsolete 1980s style OO framework, some perl-style features that make code slightly faster to write and much, much harder to read, and templates. Templates are good, but not good enough to justify the rest of the crap.
Anyway. I'm probably preaching to the choir again.
With regard to variables static to a file… I think gdb has pretty good support for those. I really like using them. Unlike private variables in C++, symbols static to a file really *are* hidden.
C.
Appreciate this site – extremely user-friendly and lots to see!
5/14/2019 @ 8:35:12 PM Like yosefk.com– extremely informative and a lot of stuff to see!
I like this website its a master peace ! Glad I found this on google .
I conceive you have mentioned some very interesting details , appreciate it for the post.
I am not rattling great with English but I get hold this really easygoing to read .
I simply must tell you that you have an excellent and unique website that I really enjoyed reading.
I am glad to be one of the visitors on this great website (:, appreciate it for posting .
I like this article, because so much useful stuff on here : D.
I must say got into this site. I found it to be interesting and loaded with unique points of view.
I must say, as a lot as I enjoyed reading what you had to say, I couldnt help but lose interest after a while.
Your article has proven useful to me.
I am glad to be one of the visitors on this great website (:, appreciate it for posting .
I truly enjoy looking through on this web site , it holds superb content .
Awesome, this is what I was searching for in google
Thank You for this.
I was looking at some of your articles on this site and I believe this internet site is really instructive! Keep on posting .
Enjoyed reading through this, very good stuff, thankyou .
I like this website its a master peace ! Glad I found this on google .
very cool post, i actually love this web site, carry on it
I must say, as a lot as I enjoyed reading what you had to say, I couldnt help but lose interest after a while.
Just wanna input on few general things, The website layout is perfect, the articles is very superb : D.
Your website has proven useful to me.
I have interest in this, xexe.
Hi, glad that i saw on this in bing. Thanks!
Awesome, this is what I was searching for in bing
I was looking at some of your articles on this site and I believe this internet site is really instructive! Keep on posting .
I truly enjoy looking through on this web site , it holds superb content .
Intresting, will come back here once in a while.
I was very pleased to discover this website.
I need to to thank you for ones time just for this wonderful read!!
I definitely enjoyed every little bit of it and i also have you saved as a favorite to
check out new stuff on your web site.
Good Morning, google lead me here, keep up great work.
An impressive share! I have just forwarded this onto a friend who has been doing a little homework on this.
And he actually ordered me breakfast due to the fact that
I stumbled upon it for him… lol. So allow me
to reword this…. Thanks for the meal!! But yeah, thanx for spending the time to talk about this matter
here on your website.
You got yourself a new follower.
Enjoyed reading through this, very good stuff, thankyou .
I love reading through and I believe this website got some genuinely utilitarian stuff on it! .
Very interesting points you have remarked, appreciate it for putting up.
Good info. Lucky me I discovered your blog by accident (stumbleupon).
I have saved it for later!
Morning, here from bing, i enjoyng this, will come back soon.
Enjoyed reading through this, very good stuff, thankyou .
In my estimation, yosefk.com does a good job of covering subject matter like this! Even if sometimes deliberately contentious, the material posted is more often than not thoughtful and challenging.
Wow, awesome weblog layout! How lengthy have you ever been running a blog for?
you make running a blog look easy. The total look of your site
is wonderful, let alone the content material!
Some truly good stuff on this web site , appreciate it for contribution.
This is my first time visit at here and i am in fact pleassant to read
all at alone place.
Found this on bing and I’m happy I did. Well written web.
Heya are using WordPress for your site platform? I'm new to the
blog world but I'm trying to get started and set up my own. Do you require
any html coding knowledge to make your own blog?
Any help would be really appreciated!
I like this page, useful stuff on here : D.
Highly descriptive post, I liked that a lot.
Will there be a part 2?
This information is invaluable. When can I find out more?
Wonderful blog! I found it while browsing on Yahoo News. Do
you have any suggestions on how to get listed in Yahoo News?
I've been trying for a while but I never seem to get there!
Thank you
Hey! I just wanted to ask if you ever have any issues with hackers?
My last blog (wordpress) was hacked and I ended up losing several weeks of hard work due to no
back up. Do you have any solutions to stop hackers?
6/5/2019 @ 11:52:44 PM Love the site– very user-friendly and tons of stuff to consider!
Thank you for some other great post. Where else could anyone
get that kind of info in such a perfect method of writing?
I have a presentation subsequent week, and I'm at the search for such info.
It's hard to come by well-informed people about this topic, however, you seem like you know what you're talking about!
Thanks
Simply desire to say your article is as astounding.
The clarity on your submit is simply great
and i can assume you're a professional in this subject.
Well with your permission allow me to clutch your feed to stay updated with imminent post.
Thank you a million and please continue the enjoyable work.
Saved as a favorite, I love your site!
That is very interesting, You're a very skilled blogger.
I have joined your rss feed and sit up for in the hunt for extra of your
great post. Additionally, I have shared your website in my social networks
It's very trouble-free to find out any matter on net as compared to textbooks, as I found this post at this website.
Some truly wonderful content on this web site , appreciate it for contribution.
My brother recommended I would possibly like this blog.
He used to be entirely right. This publish actually made my day.
You can not imagine simply how a lot time I had spent for this info!
Thank you!
Enjoyed reading through this, very good stuff, thankyou .
Awesome, this is what I was looking for in google
stays on topic and states valid points. Thank you.
You are so interesting! I don't believe I've truly read something like this before. So nice to find someone with a few original thoughts on this topic. Really.. many thanks for starting this up. This web site is one thing that is required on the web, someone with a little originality!
This does interest me
Yeah bookmaking this wasn’t a risky decision outstanding post! .
This is awesome!
Ha, here from yahoo, this is what i was looking for.
I’m impressed, I have to admit. Genuinely rarely should i encounter a weblog that’s both educative and entertaining, and let me tell you, you may have hit the nail about the head. Your idea is outstanding; the problem is an element that insufficient persons are speaking intelligently about. I am delighted we came across this during my look for something with this.
Enjoyed reading through this, very good stuff, thankyou .
Cheers, great stuff, I enjoying.
I really enjoy examining on this web , it has got fine goodies .
This does interest me
Heya i'm for the first time here. I found this board and I find
It really useful & it helped me out much. I hope to give
something back and aid others like you helped me.
I like this site because so much useful stuff on here : D.
Deference to op , some superb selective information .
Hey, happy that i found on this in bing. Thanks!
I simply must tell you that you have an excellent and unique post that I kinda enjoyed reading.
Morning, i really think i will be back to your site
I really enjoy examining on this internet site , it has got good posts .
I really enjoy examining on this website , it has got fine goodies .
Found this on google and I’m happy I did. Well written site.
Great, this is what I was looking for in google
Parasite backlink SEO works well :)
I’m impressed, I have to admit. Genuinely rarely should i encounter a weblog that’s both educative and entertaining, and let me tell you, you may have hit the nail about the head. Your idea is outstanding; the problem is an element that insufficient persons are speaking intelligently about. I am delighted we came across this during my look for something with this.
I conceive you have mentioned some very interesting details , appreciate it for the post.
I kinda got into this site. I found it to be interesting and loaded with unique points of view.
I was looking at some of your articles on this site and I believe this internet site is really instructive! Keep on posting .
Great article to check out, glad that duckduck brought me here, Keep Up cool job
Ha, here from yahoo, this is what i was looking for.
Good Morning, yahoo lead me here, keep up good work.
Some truly cool goodies on this web site , appreciate it for contribution.
Great article to see, glad that Yahoo led me here, Keep Up great job
Zithromax Azithromycin Online [url=http://buyoxys.com]wheretobuylevitrapills[/url] Viagra 2 Day Delivery Cialis Forocoches Contact Customer Ed
We're a bunch of volunteers and opening a brand new scheme in our community.
Your site provided us with useful info to work on. You've done an impressive activity and our entire neighborhood might be grateful to you.
Thanks for sharing your thoughts. I really appreciate your efforts and I will be waiting for your further post thank you once again.
Kamagra Impacto [url=http://leviprices.com]best prices for on line levitra[/url] Priligy Expedition Rapide Viagra Duree
Hello everyone, it's my first pay a quick visit at this website, and
post is really fruitful for me, keep up posting these types of articles.
Very interesting points you have remarked, appreciate it for putting up.
Hey There. I found your blog using msn. This is a really well written article.
I will be sure to bookmark it and return to
learn extra of your helpful info. Thanks for
the post. I'll certainly comeback.
Appreciate it for this howling post, I am glad I observed this internet site on yahoo.
WOW just what I was looking for. Came here by searching for natalielise pof natalielise
Your post has proven useful to me.
Thanks for sharing your thoughts
I was looking at some of your articles on this site and I believe this internet site is really instructive! Keep on posting .
Hello there, I discovered your blog via Google even as
looking for a similar subject, your web site got here
up, it appears great. I've bookmarked it in my google bookmarks.
Hi there, just turned into aware of your blog through Google,
and found that it is really informative. I'm going
to be careful for brussels. I will be grateful for those who continue this in future.
A lot of people will likely be benefited from your writing.
Cheers!
Simply desire to say your article is as amazing. The clarity in your post is just spectacular and i could assume you're
an expert on this subject. Fine with your permission allow me to grab your feed
to keep updated with forthcoming post. Thanks a million and
please carry on the rewarding work. pof natalielise
I dugg some of you post as I thought they were very beneficial invaluable
Wonderful, what a website it is! This website gives useful
data to us, keep it up.
Hello dear, I raed your whole content is very awesome, keep it up, Thanks for share this article such a nice informative post, keep up the good work.
Really dear, I like your article it is easy to reading. You are a good blogger pls keep it up. Thank you so much for this type of informaton.
Your blog is very nice and amazing information for all of us. Be continue and do best always. thanks for sharing.
thanks you so much dear, I found bestest ever information on your blog , keep sharing and blogging.
Great work !! Looking forward to more of such good work, Thanks for sharing this helpful information with us.
Wow it is extremely superb and great along these lines it is particularly helpful for me to comprehend numerous ideas and helped me a ton. it is extremely logical best and I got more information from your website
I just want to say I am very new to blogs and truly savored you’re web site. More than likely I’m likely to bookmark your website . You amazingly come with superb articles and reviews. Regards for sharing your webpage.
Usually I never comment on blogs but your article is so convincing that I never stop myself to say something about it. You’re doing a great job Man,Keep it up.
Great work !! Looking forward to more of such good work, Thanks for sharing this helpful information with us.
Nice post thanks for sharing, i really liked this. Keep this good work doing, I’ve Bookmarked your website for future visit
Great blog! Thanks for sharing this beautiful information, so share more contents like that, for more details visit us.
You are a really good blogger. You shared nice information for everyone and many people have to trust on your website like an informative article. Please keep it up for the 5 best content.
Wow, I really liked this page. As a beginner, it really gave me a lot of strength.
clients confront the issue with troubleshooting fo activation or installation of the ESPN program. In this circumstance, we'll give you with comprehensive support together with the instructions on how to include ESPN in your Roku device station list and to receive it triggered to stream live broadcasts.you can trigger to go to espn.com/activate.
Go to office.com/setup and sign in to your Microsoft account. Now, download the MS Office setup file. Install and activate it using the MS Office setup activation key, visit
Nice Article! Thanks for sharing this beautiful information, so share more contents like that, for more details visit us.